Neutrino Docs
SDK Reference

@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 characters
import { 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 fails
import { 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 type
import { 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.

TypeDefinitionDescription
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.

ConstantValueDescription
NNO_ID_LENGTH10Length of every NanoID (platform, tenant, stack IDs)
CF_RESOURCE_NAME_MAX_LENGTH63Cloudflare 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

ConstantValueDescription
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 UUID

buildTracingHeaders(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):

  1. signature is non-empty — missing_signature
  2. timestamp is non-empty — missing_timestamp
  3. timestamp is within maxAgeSeconds of now — timestamp_expired
  4. 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.

On this page