OpenMesh
docs 0.5
OpenMesh 0.5 documentation

Start as an API.
Grow into a mesh.

OpenMesh is a TypeScript service runtime for Node.js. Use the native HTTP runtime by itself, add your existing database client, then turn on discovery, traffic policy, workload identity and service-to-service calls only when the application needs them.

0.5.0 current Node.js ≥ 22 ESM + CommonJS strict TypeScript

A useful service in five minutes.

The default path is ordinary HTTP. No sidecar, controller class or custom query layer is required.

npm install openmesh-node
server.ts
import openmesh, { created } from 'openmesh-node';
import { bodyParser } from 'openmesh-node/plugins';

const app = openmesh();

app.use(bodyParser());

app.post('/users/:id', {
  body: NewUser,
  response: {
    201: User,
    409: Problem
  }
}, async ({ body, params }) => {
  return created(await users.create({
    id: params.id,
    ...body
  }));
});

await app.listen({ port: 3000 });
What OpenMesh is doing here

Routing, body parsing, Standard Schema validation, typed response contracts and graceful lifecycle all stay inside the Node.js process.

Use only the layer you need.

openmesh-nodeApplication runtime

HTTP, routing, lifecycle, typed route shorthand and application mesh facade.

openmesh-node/pluginsRequest plugins

Body parsers, health checks and ecosystem integration points.

openmesh-node/dbDatabase lifecycle

Keep Prisma, Drizzle, Kysely, TypeORM or your existing client API.

openmesh-node/meshPeer data plane

Peer selection, admission, deadlines, retries, circuits and streams.

openmesh-node/servicesControl plane

Registration, discovery, config snapshots, watches and managed pools.

openmesh-node/otelMetrics bridge

OpenTelemetry-compatible metrics without coupling core to the SDK.

Typed contracts without framework ceremony.

OpenMesh 0.5 adds Standard Schema input inference and response contracts directly to app.get/post/.... The advanced functional contract API remains optional.

Default route API

  • typed body, params, query and headers
  • runtime validation and transformation
  • compile-time response status/body contracts
  • complete HTTP lifecycle hooks
  • JSON, text, raw, form and multipart parsing

Advanced contract API

Use openmesh-node/http when routes need to exist as immutable values for tooling, client generation, policy inspection or contract-first tests.

Advanced HTTP contracts ↗

Your ORM remains your ORM.

openmesh-node/db does not invent a query language. It gives your existing client application lifecycle, readiness and an optional typed transaction boundary.

database.ts
import { database } from 'openmesh-node/db';

const db = database(prisma, {
  name: 'primary',
  connect: client => client.$connect(),
  disconnect: client => client.$disconnect(),
  transaction: (client, work) => client.$transaction(work)
});

app.register(db);

app.get('/users', async () => db.client.user.findMany());
Prisma, Drizzle, Kysely, TypeORM and lifecycle details ↗

The service relationship becomes runtime state.

Configure the application once, then use lazy app.mesh(name) handles. The first real call creates one managed service pool and reuses its discovery, pressure and failure state.

gateway.ts
const app = openmesh({
  service: 'gateway',
  mesh: {
    control: {
      url: process.env.OPENMESH_CONTROL_URL!,
      token: process.env.OPENMESH_TOKEN!
    }
  }
});

const payments = app.mesh('payments');

const charge = await payments.post('/charges', {
  key: user.id,
  body: { userId: user.id, amount: order.total }
});
discovery→traffic target→peer selection→admission→deadline + retry→transport
Application-native mesh deep dive ↗

Choose the subset before choosing the peer.

0.5 separates traffic targeting from peer selection. That keeps canaries, regional failover and affinity composable with the same retries, circuits and concurrency controls.

Header routes

Ordered rules can select a subset from inbound request metadata without blindly forwarding those headers.

Weighted subsets

Split traffic across version/metadata groups. A request key keeps weighted selection sticky.

Locality failover

Prefer zones or regions in order, then move outward only when eligible local peers are unavailable.

Live policy

Control-plane config revisions can replace traffic policy without rebuilding the service pool or restarting the process.

mTLS identity belongs to the application boundary.

A service can bind itself to a SPIFFE-style identity. OpenMesh validates the local certificate before startup, requires authenticated inbound peers and verifies the expected service identity on outbound mesh calls.

identity.ts
const payments = openmesh({
  service: 'payments',
  identity: {
    trustDomain: 'mesh.example.internal',
    ca,
    cert,
    key,
    allow: ['gateway']
  }
});

// Expected URI SAN:
// spiffe://mesh.example.internal/service/payments
Rotation without process restart

app.workload.rotate(...) swaps the inbound secure context for new connections and rotates outbound mesh TLS agents with an optional drain grace period.

Versioned state, push-first delivery.

Registration leases, membership revisions and configuration epoch + revision make distributed state changes explicit. Authenticated SSE watches are the steady-state path; polling remains a compatibility fallback.

registrationlease ownership + heartbeat + pre-drain removal
discoveryfull membership snapshots + monotonic revisions
configurationimmutable snapshots + epoch/revision CAS
authorizationscoped credentials by action, service and namespace
durabilityRedis adapters or your own adapter implementation
Control-plane protocol and adapters ↗

Instrumentation is opt-in, not baked into the hot path.

Application and peer lifecycle events are emitted only when observers are configured. The OpenTelemetry bridge consumes those events without making the SDK a core runtime dependency.

Inbound lifecycle

request start, finish, error and actual response completion.

Peer lifecycle

attempt, success, failure, cancellation, queue pressure and adaptive limit changes.

Trace context

request IDs and W3C traceparent propagate through AsyncLocalStorage and mesh calls.

Bounded attributes

the metrics bridge avoids raw paths, bodies, tokens and arbitrary high-cardinality values.

Observability reference ↗