@neutrino-io/core
Neutrino platform core primitives — naming, types, constants, tracing, errors, and middleware.
Package: @neutrino-io/core · Version: 0.3.2
Core primitives shared across all Neutrino services and feature packages. Prefer subpath imports for tree-shaking.
// Subpath imports (recommended)
import { generateId } from "@neutrino-io/core/naming";
import type { Environment } from "@neutrino-io/core/types";
import { NNO_ID_LENGTH } from "@neutrino-io/core/constants";
import { X_TRACE_ID } from "@neutrino-io/core/tracing";
import { ErrorCode, makeErrorEnvelope } from "@neutrino-io/core/errors";
import { verifyNnoSignature } from "@neutrino-io/core/middleware";
// Barrel import (convenience — imports everything)
import { generateId, Environment, NNO_ID_LENGTH } from "@neutrino-io/core";@neutrino-io/core/naming
Utilities for generating and validating Cloudflare-safe resource names, IDs, and hostnames. Never invent your own naming logic — always use these functions.
generateId()
Generates a Cloudflare-safe NanoID: 10 characters, [a-z0-9] only.
Use for platform IDs, tenant IDs, and sub-tenant IDs.
function generateId(): string;import { generateId } from "@neutrino-io/core/naming";
const platformId = generateId(); // e.g. "k3m9p2xw7q"buildResourceName(parts)
Builds a Cloudflare resource name for a client platform resource.
Format: {platformId}-{stackId}-{service}[-resourceType][-stg]
Production names carry no suffix. Staging names end with -stg.
interface ResourceNameParts {
platformId: string; // 10-char nano-id
stackId: string; // 10-char nano-id or "default"
service: string; // service slug, e.g. "auth"
resourceType?: string; // "db" | "storage" | "kv" | "queue"
staging?: boolean;
}
function buildResourceName(parts: ResourceNameParts): string;
// throws Error if name exceeds 63 chars or contains invalid charactersimport { buildResourceName } from "@neutrino-io/core/naming";
buildResourceName({
platformId: "k3m9p2xw7q",
stackId: "ab1cd2ef3g",
service: "auth",
});
// → "k3m9p2xw7q-ab1cd2ef3g-auth"
buildResourceName({
platformId: "k3m9p2xw7q",
stackId: "ab1cd2ef3g",
service: "auth",
resourceType: "db",
staging: true,
});
// → "k3m9p2xw7q-ab1cd2ef3g-auth-db-stg"buildNnoResourceName(parts)
Builds a Cloudflare resource name for a Neutrino-level resource.
Format: nno-{platformId}-{service}[-resourceType][-stg]
interface NnoResourceNameParts {
platformId: string;
service: string;
resourceType?: string; // "db" | "storage" | "kv" | "queue"
staging?: boolean;
}
function buildNnoResourceName(parts: NnoResourceNameParts): string;
// throws Error if validation failsimport { buildNnoResourceName } from "@neutrino-io/core/naming";
buildNnoResourceName({
platformId: "k3m9p2xw7q",
service: "registry",
resourceType: "db",
});
// → "nno-k3m9p2xw7q-registry-db"parseResourceName(name)
Parses a client resource name back into its components.
interface ParsedResourceName {
platformId: string;
stackId: string;
service: string;
resourceType?: string;
isStaging: boolean;
}
function parseResourceName(name: string): ParsedResourceName | null;Returns null if the name does not match the expected format.
parseNnoResourceName(name)
Parses a Neutrino resource name back into its components.
interface ParsedNnoResourceName {
platformId: string;
service: string;
resourceType?: string;
isStaging: boolean;
}
function parseNnoResourceName(name: string): ParsedNnoResourceName | null;Returns null if the name does not start with nno- or fails validation.
validateResourceName(name)
Validates a Cloudflare resource name against platform constraints.
Rules: max 63 chars, only [a-z0-9-], cannot start or end with a hyphen.
interface ValidationResult {
valid: boolean;
error?: string;
}
function validateResourceName(name: string): ValidationResult;validateId(id)
Validates a NanoID (platform ID or entity ID).
Must be exactly 10 characters, [a-z0-9] only.
function validateId(id: string): ValidationResult;buildHostname(opts)
Builds a fully-qualified *.nno.app hostname for a client platform service.
Format: {name}.{type}[.stg].{stackId}.{platformId}.nno.app
interface BuildHostnameOpts {
name: string; // DNS label, e.g. "api"
type: "app" | "svc";
stackId: string; // 10-char nano-id or "default"
platformId: string; // 10-char nano-id
staging?: boolean;
}
function buildHostname(opts: BuildHostnameOpts): string;
// throws Error on invalid DNS label, stackId, platformId, or typeimport { buildHostname } from "@neutrino-io/core/naming";
buildHostname({
name: "api",
type: "svc",
stackId: "ab1cd2ef3g",
platformId: "k3m9p2xw7q",
});
// → "api.svc.ab1cd2ef3g.k3m9p2xw7q.nno.app"
buildHostname({
name: "api",
type: "svc",
stackId: "ab1cd2ef3g",
platformId: "k3m9p2xw7q",
staging: true,
});
// → "api.svc.stg.ab1cd2ef3g.k3m9p2xw7q.nno.app"buildNnoHostname(opts)
Builds a *.nno.app hostname for a Neutrino core service.
Format: {name}.{type}[.stg].nno.app
interface BuildNnoHostnameOpts {
name: string;
type: "app" | "svc";
staging?: boolean;
}
function buildNnoHostname(opts: BuildNnoHostnameOpts): string;buildNnoHostname({ name: "registry", type: "svc" });
// → "registry.svc.nno.app"parseHostname(hostname)
Parses a *.nno.app hostname back into its components.
interface ParsedHostname {
name: string;
type: "app" | "svc";
stackId: string | null; // null for Neutrino core hostnames
platformId: string | null; // null for Neutrino core hostnames
isStaging: boolean;
isNnoCore: boolean;
}
function parseHostname(hostname: string): ParsedHostname | null;Returns null if the hostname does not end with .nno.app or has an unrecognised structure.
isValidUserStackId(id)
Returns true if id is a valid user-created stack ID (10-char nano-id). Returns false for "default" — the reserved built-in stack name.
function isValidUserStackId(id: string): boolean;@neutrino-io/core/types
Shared TypeScript types used across all Neutrino packages.
| Type | Definition | Description |
|---|---|---|
Environment | "dev" | "stg" | "prod" | Deployment environment |
EntityType | "platform" | "tenant" | "subtenant" | Entity hierarchy level |
ValidationResult | { valid: boolean; error?: string } | Result from validators |
import type {
Environment,
EntityType,
ValidationResult,
} from "@neutrino-io/core/types";@neutrino-io/core/constants
Shared platform constants.
| Constant | Value | Description |
|---|---|---|
NNO_ID_LENGTH | 10 | Length of every NanoID (platform, tenant, stack IDs) |
CF_RESOURCE_NAME_MAX_LENGTH | 63 | Cloudflare resource name character limit |
import {
NNO_ID_LENGTH,
CF_RESOURCE_NAME_MAX_LENGTH,
} from "@neutrino-io/core/constants";@neutrino-io/core/tracing
Header name constants and helpers for distributed tracing. Import these everywhere — never hardcode header strings.
Constants
| Constant | Value | Description |
|---|---|---|
X_REQUEST_ID | "X-Request-Id" | Unique ID for a single HTTP request |
X_TRACE_ID | "X-Trace-Id" | Distributed trace identifier, propagated across services |
X_SPAN_ID | "X-Span-Id" | Identifier for a single span within a trace |
Types
type TracingHeaderName = "X-Request-Id" | "X-Trace-Id" | "X-Span-Id";
interface TracingHeaders {
"X-Request-Id"?: string;
"X-Trace-Id"?: string;
"X-Span-Id"?: string;
}
interface SpanContext {
requestId: string;
traceId: string;
spanId: string;
}extractSpanContext(headers)
Extracts tracing context from incoming HTTP headers. Generates new UUIDs for any missing values. Always generates a fresh spanId per service hop.
function extractSpanContext(headers: {
get(name: string): string | null;
}): SpanContext;import { extractSpanContext, X_TRACE_ID } from "@neutrino-io/core/tracing";
const ctx = extractSpanContext(request.headers);
// ctx.requestId — from X-Request-Id or new UUID
// ctx.traceId — from X-Trace-Id or new UUID
// ctx.spanId — always a new UUIDbuildTracingHeaders(ctx)
Builds a headers record from a SpanContext for forwarding to upstream services.
function buildTracingHeaders(ctx: SpanContext): Record<string, string>;@neutrino-io/core/errors
Canonical Neutrino API error envelope and error codes.
ErrorCode
Common error codes used across all Neutrino services.
const ErrorCode = {
UNAUTHORIZED: "UNAUTHORIZED",
FORBIDDEN: "FORBIDDEN",
VALIDATION_ERROR: "VALIDATION_ERROR",
INVALID_REQUEST: "INVALID_REQUEST",
NOT_FOUND: "NOT_FOUND",
CONFLICT: "CONFLICT",
INTERNAL_ERROR: "INTERNAL_ERROR",
SERVICE_UNAVAILABLE: "SERVICE_UNAVAILABLE",
TIMEOUT: "TIMEOUT",
RATE_LIMITED: "RATE_LIMITED",
} as const;
type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];NnoErrorEnvelopeSchema / NnoErrorEnvelope
Zod schema and inferred type for the canonical Neutrino API error response shape. All Neutrino services return errors in this envelope.
// Wire shape
{
"error": {
"code": "NOT_FOUND",
"message": "Platform not found",
"details": { "platformId": "k3m9p2xw7q" }, // optional
"requestId": "req_abc123" // optional
}
}import { NnoErrorEnvelopeSchema } from "@neutrino-io/core/errors";
import type { NnoErrorEnvelope } from "@neutrino-io/core/errors";
// Validate an unknown response body
const result = NnoErrorEnvelopeSchema.safeParse(body);makeErrorEnvelope(code, message, options?)
Constructs a well-formed NnoErrorEnvelope object.
function makeErrorEnvelope(
code: string,
message: string,
options?: { details?: Record<string, unknown>; requestId?: string },
): NnoErrorEnvelope;import { makeErrorEnvelope, ErrorCode } from "@neutrino-io/core/errors";
return c.json(
makeErrorEnvelope(ErrorCode.NOT_FOUND, "Platform not found", {
details: { platformId },
requestId: c.get("requestId"),
}),
404,
);@neutrino-io/core/middleware
HMAC-SHA256 signature verification for upstream service-to-service calls.
The gateway injects an X-NNO-Signature header on every proxied request. Upstream services use verifyNnoSignature to confirm the request originated from the gateway and has not been tampered with.
verifyNnoSignature(opts)
Verifies an X-NNO-Signature HMAC header. Uses constant-time comparison to prevent timing attacks.
Checks (in order):
signatureis non-empty —missing_signaturetimestampis non-empty —missing_timestamptimestampis withinmaxAgeSecondsof now —timestamp_expired- Reconstructed HMAC matches provided signature —
invalid_signature
interface VerifySignatureOptions {
secret: string; // NNO_INTERNAL_API_KEY shared secret
signature: string; // X-NNO-Signature header value
timestamp: string; // X-NNO-Timestamp header value (unix seconds)
nnoUserId: string;
nnoRole: string;
nnoPlatformId: string;
requestId: string;
maxAgeSeconds?: number; // default: 300 (5 minutes)
}
interface VerifyResult {
valid: boolean;
reason?:
| "missing_signature"
| "missing_timestamp"
| "timestamp_expired"
| "invalid_signature";
}
function verifyNnoSignature(
opts: VerifySignatureOptions,
): Promise<VerifyResult>;import { verifyNnoSignature } from "@neutrino-io/core/middleware";
const result = await verifyNnoSignature({
secret: env.NNO_INTERNAL_API_KEY,
signature: c.req.header("X-NNO-Signature") ?? "",
timestamp: c.req.header("X-NNO-Timestamp") ?? "",
nnoUserId: c.req.header("X-NNO-User-Id") ?? "",
nnoRole: c.req.header("X-NNO-Role") ?? "",
nnoPlatformId: c.req.header("X-NNO-Platform-Id") ?? "",
requestId: c.get("requestId"),
});
if (!result.valid) {
return c.json(
makeErrorEnvelope("UNAUTHORIZED", "Invalid gateway signature"),
401,
);
}computeNnoSignature(key, payload)
Computes an HMAC-SHA256 signature over payload using key. Returns a lowercase hex-encoded 64-character string.
function computeNnoSignature(key: string, payload: string): Promise<string>;This is the same algorithm used by services/gateway. Exposed for testing and debugging.