Shared NestJS container baseline
@appneurox/service-runtime is the common HTTP foundation for catalogue service repositories. It starts one Node 24 process on port 8080; run as a nonroot user in the deployment container. The hosting edge terminates HTTPS. This package does not implement catalogue business operations, proxy existing backends, provision databases, or claim provider readiness.
Public responses label the deployment runtime: "BOILERPLATE", businessImplementation: "not_implemented", and businessReady: false. /health/ready measures HTTP bootstrap and drain state only. /health/business returns 501. Capability metadata always forces implementation: "not_implemented", even if a supplied file requests a different status.
Standalone entry
Build and test on the remote runner, without local Docker or dependency installation:
npm ci --ignore-scripts
npm test
SERVICE_NAME=example-service SERVICE_CAPABILITY_LABEL="Example capability" node dist/main.js
npm ci uses the committed lockfile. Do not publish credentials in repository files, CI output, images, or capability metadata. Runtime credentials belong in platform secrets. The package is private and is vendored into each service repository as .platform/nestjs/.
Repository root invocation after installing/building this package remotely:
node .platform/nestjs/dist/main.js
Settings
| Setting | Default and validation |
|---|---|
SERVICE_NAME | Required lowercase kebab slug, for example example-service |
SERVICE_CAPABILITY_LABEL | Display label; defaults to the service name |
CATALOG_SLUG | Service name with a trailing -service removed |
SERVICE_VERSION | 0.1.0-boilerplate |
BUILD_SHA | unknown; otherwise 7–64 hexadecimal characters |
NODE_ENV | production; also accepts development, test |
PORT, HOST | 8080, 0.0.0.0; explicit loopback/IPv6 bind addresses also accepted |
MAX_BODY_BYTES | 65536; range 1024–1048576; compressed JSON is rejected |
REQUEST_TIMEOUT_MS | 15000; range 1000–60000; bounds headers and socket inactivity |
SHUTDOWN_TIMEOUT_MS | 20000; range 1000–60000 |
LOG_LEVEL | info; debug, warn, error, silent also accepted |
CORS_ORIGINS | Empty; otherwise comma-separated exact HTTPS origins, no wildcard or credentials |
EXISTING_BACKEND_URL | Optional HTTPS reference only, without userinfo, query or fragment |
SERVICE_METADATA_FILE | Optional UTF-8 JSON file, at most 64 KiB |
SERVICE_CAPABILITIES_JSON | Alternative inline metadata JSON; mutually exclusive with the file |
Metadata file format, containing public product information only:
{
"capabilities": [
{ "id": "example-operation", "name": "Example operation", "description": "Planned business capability." }
]
}
Without a file or inline JSON, the package exposes one pending capability from CATALOG_SLUG and SERVICE_CAPABILITY_LABEL. Maximum 500 capability entries; duplicate IDs and invalid names fail startup. Invalid settings report setting names, never supplied values.
Endpoints
| Path | Meaning |
|---|---|
/ | Service identity and explicit boilerplate disclosure |
/version | Version/build identity and pending business status |
/health/live | Process can answer HTTP |
/health/ready | HTTP runtime initialized and not draining, 200/503 |
/health/business | Business capability unimplemented, 501 |
/v1/capabilities | Planned capabilities and common baseline metadata |
All other registered controllers require verified authentication unless explicitly annotated @Public(). Unregistered paths return a normalized 404. The baseline never interprets any shared token, a tenant header, a decoded JWT, or a frontend session as verified identity.
Extend a service
import { bootstrapService, RequireScopes, RequireTenant } from '@appneurox/service-runtime';
import type { AuthVerifier } from '@appneurox/service-runtime';
import { RecordsModule } from './records/records.module';
// Implement this port using the actual identity authority. Verify signature,
// issuer, audience and expiry; resolve membership/scopes. Invalid token -> null.
// Provider/authority outage -> throw. No reference verifier is fabricated here.
declare const verifier: AuthVerifier;
void bootstrapService({ imports: [RecordsModule], authVerifier: verifier });
Use @RequireTenant() and @RequireScopes('records:read') on protected routes. X-Tenant-ID is an untrusted selection until the guard confirms membership in the verified principal's tenantIds; no tenant header creates authority. A principal with exactly one tenant may select it implicitly; multiple tenants require selection on tenant-required routes. Scopes match exactly, with no automatic wildcard escalation. The verifier receives the raw bearer token privately and must not log it. Protected routes return 401 without credentials, 503 when no verifier is configured, and 403 for mismatched membership/scopes.
CurrentPrincipal, CurrentTenant, requestContext, and SERVICE_CONFIG are exported for business code. Scope every repository query/write to the authenticated tenant; this HTTP contract does not substitute for database isolation or resource authorization. Business modules must implement their real dependency probes, OpenAPI contracts, storage/migrations, transactions/idempotency, provider integrations, authorization, telemetry, and functional tests separately. Preserve existing business backends while adopting common code; this package is a starter, not an automatic migration.
createServiceApplication() initializes without listening or signal handlers for tests/embedding. bootstrapService() listens, handles SIGTERM/SIGINT, marks draining before close, and forces exit after the shutdown deadline. It returns { app, config, shutdown }; programmatic shutdown is idempotent. Use handleSignals: false when the host owns lifecycle handling.
Strict DTO validation uses class-validator metadata: unknown properties and implicit coercion are rejected, and validation values are excluded from responses. Request/correlation IDs propagate after syntax checks; duplicate security headers are rejected. Logs include method, route template, duration, status and IDs, excluding raw URLs, headers, bodies and queries. Security headers, disabled proxy-header trust, JSON limits and generic exceptions apply to every controller. Apply authentication-aware rate limits at the edge and/or a real shared store; this starter does not pretend an in-memory counter is a fleet-wide quota.
Verification
npm test compiles TypeScript then runs Node's test runner. Tests cover metadata honesty, configuration limits, redaction, public health semantics, auth default denial, tenant/scopes, request-local context, DTO rejection, malformed/oversize/compressed JSON, duplicate headers, security headers, CORS, and secret-free exceptions/logs. Dependency installation and executable tests are intentionally performed on the remote CI runner for this fleet.