AppPort Services
AppPort Services provides durable operational application capabilities that sit beside AuthPort.
The service set includes API Keys, Jobs, Schedules, Secrets, Webhooks, Files, and Notifications. Notifications are stored and delivered by AppPort Services; they are not an attention-management layer. Attn may consume them to derive attention separately.
Create and run an application
For a new application:
npx create-appport my-app
cd my-app
npm run dev
The generated source contains business handlers only. It does not create an HTTP server, configure persistence, implement CORS/SSE, run job workers, deliver webhooks, or install signal handlers.
For an existing application:
Install the package in your application:
npm install @appport/runtime
Initialize AppPort with every capability, or select only what the application uses:
npx @appport/runtime init
npx @appport/runtime init --use api,webhooks,jobs
With no flags, init asks which capabilities to enable and defaults each one to yes. The --use form is available for scripts and CI.
This creates two files that should be committed:
appport.tomldeclares the AppPort capabilities your application uses.feltdb.flowis your application's authoritative FeltDB contract. AppPort's internal template remains inside the npm package; application developers do not edit or import AppPort storage internals.
Bootstrap AppPort from the contract:
import { appport } from '@appport/runtime';
const app = await appport();
// Only capabilities declared in appport.toml are initialized.
await app.api.keys.createApiKey(/* ... */);
appport() reads ./appport.toml by default and owns service construction, persistence, audit infrastructure, and lifecycle. Call await app.close() during graceful shutdown. Accessing an undeclared capability throws a CapabilityNotDeclaredError with the declaration needed to enable it.
appport.toml is the authoritative application contract. It is parsed, validated, normalized, and frozen once at startup. It declares application identity, deployment and state authority, tenancy, HTTP/CORS, API keys, webhook delivery, job types, events, authorization, observability, lifecycle, and development defaults. The sibling feltdb.flow is deployed into FeltDB as the authoritative state contract.
Legacy files containing only use api, use webhooks, and use jobs remain supported. Expand one to the canonical contract with a recoverable backup using:
npx @appport/runtime config migrate
Application code supplies behavior:
import { appport } from '@appport/runtime';
const application = await appport({
routes: {
'POST /invoices': async ({ body, tenantId }) => createInvoice(body, tenantId),
},
jobs: {
'invoice.process': async (job) => processInvoice(job.payload),
},
});
await application.publish('invoice.created', { id: 'inv-123' });
The public application.state and application.events APIs provide provider-neutral state and subscriptions. Application code never reaches through a capability to access its private database.
The runtime owns the stable management contract:
/_appport/health
/_appport/overview
/_appport/capabilities
/_appport/events
/_appport/api/keys
/_appport/webhooks
/_appport/jobs
Health is public. /_appport/capabilities is the unauthenticated discovery surface and returns the mounted capability catalog for the running application. Other endpoints follow the contract's authorization and tenant rules. Events uses SSE for GET and publishes domain events with POST; API keys, webhooks, and jobs support runtime-owned create/list operations.
@appport/services supplies the CLI and capability implementation, but application source imports only @appport/runtime. Existing applications may continue using createServices() from @appport/services as a compatibility API.
Run your application with its usual command, such as npm run dev. Operational CLI commands are available through the installed binary:
npx @appport/runtime api-key list --tenant acme
npx @appport/runtime webhook list --tenant acme
npx @appport/runtime job list --tenant acme
Standalone management host
You do not need an application to operate AppPort Services. serve starts the management host, which owns its durable state, ServiceGateway, authentication, management router, and HTTP server:
npx @appport/services init # appport.toml + feltdb.flow for this deployment
npx @appport/services serve # GET /v1/ui, /services, and the other management pages
It binds 127.0.0.1:4100 by default; override with --host / --port. State is the same durable FeltDB deployment the CLI commands use, so it survives restart. Management requests authenticate with an AppPort API key (Authorization: Bearer <secret>) or an operator identity adapter; authorization stays with AuthBoundry. See docs/management.md.
AppPort manages its FeltDB runtime dependency; consumers do not import @feltdb/core or AppPort's internal stores.
The repository defines AppPort capabilities for tenant-scoped API keys, durable webhooks, durable job execution (including schedules), provider-neutral Secrets metadata/lifecycle and scoped-resolution contracts, durable notifications, and files. Legacy runtime state uses @feltdb/core (the host's copy, 0.16.1 or later 0.x). Secret material remains with an authorized provider and is never durable AppPort state.
Application
│
┌───────────┴───────────┐
│ │
AuthPort AppPort Services
│ │
identity/authz API Keys, Webhooks, Jobs, Secrets
│ │
└───────────┬───────────┘
│
FeltDB
Consumer API
Sharing the host application's FeltDB instance
A host that already owns its application's FeltDB instance can run every service on it, instead of letting AppPort Services open a second instance:
import { createFeltDB } from '@feltdb/core';
import { createServices } from '@appport/services';
const db = createFeltDB({ mode: 'local', namespace: 'invoices', path: './state' });
const services = createServices({ db, application: 'invoices', authorizer: authBoundry });
// Service stores, effect evidence, and services.transaction() all use `db`.
The host keeps ownership: AppPort Services never closes an instance it was given. The
instance's own deployment mode (db.capabilities().deployment) is used for
mode-dependent behavior. Use one copy of @feltdb/core for the host and this package: it is a
peer dependency (>=0.16.1 <1.0.0), so the host's copy is the one Services uses, and npm installs
no second one.
To check which instance a services object runs on (local, memory, or remote), use
services.db (read-only) or services.usesInstance(db), which compares identity;
services.ownsDb is false for a host-supplied instance:
assert.ok(services.usesInstance(db));
Version, health, evidence, and configuration transfer
import { VERSION } from '@appport/services'; // equals package.json "version"
import manifest from '@appport/services/package.json' with { type: 'json' };
const health = await services.health({ tenantId: 'acme' });
// health.status: 'ok' | 'degraded' | 'unconfigured'; health.services.webhooks.state; health.attention.failedWebhookDeliveries
const principal = services.identify({ principalId, principalType: 'user', tenantId, runId: requestId });
await services.invoke('files.write', input, { principal });
const { state, records } = await services.evidence.findByRunId(requestId); // state: 'none' | 'started' | 'completed' | 'failed'
const document = await services.configuration.export({ principal, environment: 'staging' });
const report = await other.configuration.import(document, { principal, dryRun: true });
services.health()— per-service state and counts needing attention, from bounded queries. See docs/health.md.services.evidence.findByRunId()— the effect evidence recorded under a run id (the caller's correlation id), and what astartedrow means after a crash. See docs/AUTHORITY.md.services.configuration.export()/import()— non-secret configuration between environments; secret values never leave. See docs/configuration.md.webhooks.deliveries.read— its own capability for reading webhook deliveries (invoke('webhooks.deliveries.read', { status: 'failed' }),GET /_appport/webhooks/deliveries).
Existing authenticated Express applications can mount the supported management runtime against their existing service instance:
import { createManagementRouter, createServices } from '@appport/services';
const services = createServices({ path: '.appport', application: 'invoices', authorizer: authBoundry, credentials: authBoundryCustody });
app.use(createManagementRouter({
services,
authority: services.gateway,
authenticate: (request) => hostAuthentication(request), // identity only
}));
The host owns authentication, AuthBoundry owns authorization, and AppPort Services enforces AuthBoundry's decisions before any effect. @appport/services executes effects. It does not decide who is allowed to cause them. See the authority model, webhook security, and job security. API-key management requires apikeys.read, apikeys.create, and apikeys.revoke (plus apikeys.update, apikeys.rotate, and apikeys.audit for editing, rotation, and audit), always uses the authenticated tenant, and returns a plaintext credential only in creation and rotation responses. See Composable management runtime for the full contract and standalone/embedded behavior. Other packages can add their own pages, routes and capabilities to the same host through management extensions.
Outbound credentials use a server-only scoped protocol. AppPort defines the reference, context, lifecycle, audit, errors, and callback contract; AuthBoundry authorizes and AppBoundry resolves provider-held material. AppPort ships no resolver, secret store, provider adapter, or policy engine, and exposes no browser-facing credential-value route.
When the host's custody is a SecretCustody (withSecret plus store and status), credential.store collects a value (authorized, audited, versioned, never echoed or persisted in FeltDB) and hands it to the host, and credential.status reports whether material exists. The host still owns where material lives; a resolver-only host gets CUSTODY_READ_ONLY.
await secrets.withSecret({
reference: { secretId, tenantId: 'acme', provider: 'pipedrive' },
context: { tenantId: 'acme', principalId: 'integration:pipedrive', purpose: 'person.lookup', authorizationRef },
}, async ({ value }) => callProvider(value));
See the outbound credential guide for ownership, authorization handoff, lifecycle, and failure semantics.
See the capability inventory and capability authority summary for the consolidated capability matrix and mounted-state rules.
The contract determines which runtime APIs are available:
import { appport } from '@appport/runtime';
const app = await appport({ authorizer: authBoundry, credentials: authBoundryCustody });
// Identity comes from authentication; AuthBoundry authorizes each capability.
const principal = await app.authenticate(request);
// Machine identity (an API key identifies a caller; it carries no scopes)
await app.invoke('apikeys.create', { name: 'server-key' }, { principal });
// Durable outbound notifications, bound to a signing credential in AuthBoundry custody
await app.invoke('webhooks.register', {
url: 'https://acme.example.com/webhooks',
events: ['invoice.created'],
signingCredentialRef: 'credential-ref:whsec_acme',
}, { principal });
// Durable deferred execution; the job runs as this principal and is re-authorized on every run
await app.invoke('jobs.create', {
type: 'invoice.process',
payload: { invoiceId: 'inv-123' },
maxAttempts: 3,
}, { principal });
The consumer does not need to know that FeltDB exists underneath. Only declared capabilities are constructed, and they share one durable runtime.
What is AppPort Services?
AppPort Services answers:
What operational capabilities does this application expose?
AuthPort still answers identity, authentication, and authorization questions.
See It In Action
A complete example application is available under examples/services-demo/. It demonstrates:
- API key authentication with tenant-scoped principals
- Durable invoice creation with automatic webhook and job intent
- Webhook delivery with retry and replay
- Background job processing with lease-based concurrency control
- Tenant isolation and durability across process restart
See examples/services-demo/README.md for a five-minute quickstart.
Architecture
AppPort Services uses Flow (the @feltdb/core contract language) as the authoritative durable schema. The package's internal appport.flow is the template from which appport init generates the application's authoritative feltdb.flow:
API Keys vertical:
ApiKeys— API key credentials (secret stored as scrypt hash only)ApiKeyPrefixes— Prefix lookup for efficient secret validationApiKeyAuditEvents— Access audit trail
Webhooks vertical:
WebhookEndpoints— Registered webhook receiversWebhookDeliveries— Outbound delivery records with retry stateWebhookAuditEvents— Webhook lifecycle audit trail
Jobs vertical:
Jobs— Individual jobs with execution status and lease trackingJobSchedules— Recurring job definitionsJobAuditEvents— Job execution audit trail
Secrets vertical:
Secrets— Tenant-scoped logical secret identity and lifecycle metadataSecretVersions— Provider references and explicit rotation versionsSecretAuditEvents— Creation, resolution, rotation, revocation, retirement, and failure audit records
Secrets operations distinguish describeSecret (metadata only) from resolveSecret (authorized provider resolution). Secret values, plaintext, decrypted material, and provider credentials are not fields in appport.flow or audit events.
All collections are tenant-scoped via tenant_id field with tenant_idx for efficient queries.
Dependency direction
feltdb.flow (generated authoritative application contract)
↓
TypeScript implementation
↓
FeltDB (@feltdb/core, the host's copy)
The Flow contract is parsed and validated at test time. The TypeScript stores (FeltDbApiKeyStore, FeltDbWebhookEndpointStore, etc.) implement the contract semantics directly against FeltDB collections.
Why is API Keys separate from AuthPort?
API keys authenticate machine principals. They do not replace AuthPort authorization.
API key
↓
machine principal
↓
AuthPort authorization
↓
resource/action decision
Installation
npm install
npm run build
@feltdb/core is a peer dependency: install it beside Services (the latest 0.x release; npm 7 and
later install it for you), and Services uses that one copy:
{
"peerDependencies": {
"@feltdb/core": ">=0.16.1 <1.0.0"
}
}
How do I enable API keys?
Create the service with FeltDB’s real deployment model:
import { createApiKeyService } from '@appport/services';
const service = createApiKeyService({
mode: 'local',
namespace: 'appport-services',
path: './.feltdb/appport-services'
});
Remote FeltDB deployments can use the same exported deployment fields that resolveFeltDBDeployment() understands.
How do I create one?
// `operator` is a verified principal (host authentication or an existing API key).
const created = await service.createApiKey({ name: 'production' }, operator);
console.log(created.secret); // only returned once
An API key identifies a caller and its application. It carries no scopes:
AuthBoundry decides what the caller may do. Passing scopes is rejected with
a migration error (docs/AUTHORITY.md).
A key can be given restrictions, a ceiling that only narrows what AuthBoundry allows, plus an expiry. Keys can be edited, rotated with an optional overlap, revoked one at a time or in bulk, and audited:
const key = await services.apiKeys.createApiKey({
name: 'reporting-worker',
restrictions: { capabilities: ['files.read'], resources: [{ type: 'file', attributes: { owner: 'reporting' } }] },
expiresAt: new Date(Date.now() + 90 * 86_400_000),
}, operator);
const next = await services.apiKeys.rotateApiKey({ id: key.id, overlapSeconds: 300 }, operator); // next.secret shown once
await services.apiKeys.updateApiKey({ id: next!.id, name: 'reporting-worker-v2' }, operator);
const events = await services.apiKeys.listApiKeyAuditEvents({ credentialId: next!.id }, operator);
await services.apiKeys.revokeAllApiKeys({ createdBy: '[email protected]' }, operator);
Revocation and restriction changes apply on the next operation in every process. See docs/api-keys.md for the permission model, rotation semantics, verifier keys, errors, and migration notes.
CLI (the operator is identified through APPPORT_AUTHORITY or APPPORT_API_KEY):
APPPORT_AUTHORITY=./authority.mjs appport api-key create --tenant tenant-123 --name production
How does an application authenticate?
Option 1: Framework-neutral HTTP adapter
For applications using ordinary Node HTTP request/response semantics:
import { createApiKeyAuth } from '@appport/services';
const auth = createApiKeyAuth({ service });
// Optional authentication (returns null if missing/invalid)
const principal = await auth.authenticate(request);
// Required authentication (throws if missing/invalid)
const principal = await auth.require(request);
The adapter extracts the Authorization: Bearer <api-key> header and returns an AuthenticatedPrincipal:
interface AuthenticatedPrincipal {
principalId: string;
principalType: 'api_key';
tenantId: string;
applicationId: string;
credentialId: string;
verifiedBy: 'api_key';
}
Principals are branded: services accept only principals minted by an authentication path, never object literals or actor strings.
Option 2: Express middleware
For Express applications:
import { apiKeyAuth, requireApiKeyAuth } from '@appport/services';
const app = express();
// Optional authentication
app.use(apiKeyAuth(service));
app.get('/invoices', async (req, res) => {
const principal = req.auth; // null if missing/invalid
if (!principal) {
return res.status(401).json({ error: 'Unauthenticated' });
}
// Handle request with principal
});
// Or, require authentication
app.use(requireApiKeyAuth(service));
app.get('/protected', async (req, res) => {
const principal = req.auth; // guaranteed, or middleware rejects
// Handle request with principal
});
The middleware attaches the principal to req.auth and provides request-scoped context via req.authContext.
Tenant safety
If your application accepts tenant context independently, validate it against the principal:
import { assertTenant } from '@appport/services';
assertTenant(principal, tenantIdFromRequest); // throws if mismatch
Low-level Bearer token extraction
For custom frameworks:
import { authenticateBearerToken } from '@appport/services';
const principal = await authenticateBearerToken(
authorizationHeader,
service,
);
How does authorization happen?
Authentication returns an identity. Authorization belongs to AuthBoundry: every
service effect is authorized by the configured ServiceAuthorizer before it
runs, credentials are resolved only after that decision, and evidence is
written to FeltDB. See docs/AUTHORITY.md.
How do I use webhooks?
Webhooks provide durable signed delivery to external HTTP endpoints.
Setup
import { createServices } from '@appport/services';
const services = createServices({ path: './.appport', application: 'invoices', authorizer: authBoundry, credentials: authBoundryCustody });
const service = services.webhooks;
Register endpoint
const endpoint = await service.createWebhookEndpoint({
url: 'https://customer.example.com/webhooks',
events: ['invoice.created', 'invoice.paid'],
// The signing secret lives in AuthBoundry custody; the endpoint stores only the reference.
signingCredentialRef: 'credential-ref:whsec_customer',
}, principal);
// Private, loopback, link-local, and metadata destinations are rejected; redirects are never followed.
// See docs/WEBHOOK-SECURITY.md.
Emit event
await service.emitWebhookEvent({
type: 'invoice.created',
payload: { id: 'inv-456', amount: 100 }
}, principal);
// Delivery records created automatically for matching endpoints
Worker: deliver webhooks
const delivery = await service.getWebhookDelivery(tenantId, deliveryId);
const result = await service.deliverWebhook(tenantId, deliveryId);
if (result.success) {
// Delivered
} else {
// Failed or queued for retry
}
The delivery is signed with HMAC-SHA256. Each delivery includes:
{
"id": "delivery-uuid",
"eventId": "event-uuid",
"type": "invoice.created",
"data": { "id": "inv-456", "amount": 100 },
"timestamp": "2026-09-10T22:30:00Z"
}
Header: X-AppPort-Signature: <hex-encoded-hmac-sha256>
Applications read deliveries under webhooks.deliveries.read (bounded, at most 200 per call):
const failed = await services.invoke('webhooks.deliveries.read', { status: 'failed', limit: 50 }, { principal });
const one = await services.invoke('webhooks.deliveries.read', { id: deliveryId }, { principal });
Consumer verification:
import crypto from 'node:crypto';
function verify(payload, signature, secret) {
const expected = Buffer.from(crypto.createHmac('sha256', secret).update(payload).digest('hex'));
const actual = Buffer.from(signature);
return expected.length === actual.length && crypto.timingSafeEqual(expected, actual);
}
Retry policy
- 2xx → delivered
- 408 / 429 / 5xx / network failure → retry with bounded exponential backoff
- 3xx → failed (terminal): redirects are never followed
- other 4xx → failed (terminal)
- AuthBoundry denial → failed; the provider is never called
- AuthBoundry unavailable / timeout → retried later; the provider is never called
- Max attempts → 5 (configurable)
Failed deliveries can be manually replayed:
await service.replayWebhookDelivery(tenantId, deliveryId, principal); // webhooks.replay
Disable endpoint
await service.disableWebhookEndpoint({ id: endpoint.id }, principal); // webhooks.remove
// No new deliveries created; existing pending deliveries blocked
Webhooks are at-least-once delivery. Consumers should treat eventId / deliveryId as idempotency identifiers.
How do I use durable jobs?
Jobs provide reliable background task execution with retry support, concurrency control via leases, and automatic recovery from worker failure.
Setup
import { createServices } from '@appport/services';
const services = createServices({ path: './.appport', application: 'invoices', authorizer: authBoundry });
const service = services.jobs;
Register handler
service.register('invoice.process', async (job, execution) => {
const { invoiceId } = job.payload as { invoiceId: string };
// The job runs as its durable principal; every effect is authorized again.
await services.invoke('notifications.send', { recipient: 'finance', type: 'invoice.processed', title: invoiceId }, { principal: execution.principal });
});
Enqueue job
const job = await service.enqueue({
type: 'invoice.process',
payload: { invoiceId: 'inv-456' },
maxAttempts: 3,
delegationId: 'del_quote_agent', // optional AuthBoundry delegation, checked on every run
}, principal);
A job without a durable principal is never executed, and a revoked delegation stops the next run. See docs/JOB-SECURITY.md.
Execute job (worker)
const result = await service.executeJob(tenantId, jobId, 'worker-1');
if (result) {
// Job completed successfully
}
Schedule recurring job
const schedule = await service.scheduleRecurring({
type: 'invoice.reconcile',
payload: { batchSize: 100 },
interval: '1h',
}, principal);
Retry policy
- Success → job marked completed
- Failure → retry with exponential backoff (2^attemptCount)
- Max attempts exceeded → job marked failed
- Worker lease expired → job eligible for recovery by another worker
- Manual retry → reset failed job to pending state
Jobs are durable and idempotent. Job state survives process restarts; workers claim jobs via version-based optimistic locking.
How do I send notifications?
Notifications are durable application events routed through one or more delivery channels. Applications decide what happened. AppPort Services decides how a durable notification is delivered.
Application = meaning
AppPort Services = delivery infrastructure
FeltDB = durable state and evidence
Attn = attention and judgment
const { notification, deliveries } = await app.notifications.notify({
tenantId: 'tenant-a',
recipient: 'user-1',
type: 'monitor.triggered',
title: 'Status changed',
source: { type: 'monitor', id: 'monitor-7', eventId: 'observation-123' },
channels: ['browser', 'in-app'],
}, principal);
await app.notifications.markRead('tenant-a', notification.id, recipientPrincipal);
await app.notifications.acknowledge('tenant-a', notification.id, recipientPrincipal);
- Each channel has its own durable delivery record (
pending,delivered,failed,retrying). - Repeating the same source event returns the same notification.
- Retries run on the existing job infrastructure.
expiresAtstops obsolete deliveries but keeps the record as evidence.- Credentials (passwords, cookies, authorization headers, tokens, API keys, private keys) are rejected.
- Closing a browser does not destroy notifications. The browser is one delivery channel, and a new
session catches up from
GET /notifications?unread=true.
Email, mobile push, SMS, and webhook channels are adapters registered with registerChannel().
See docs/notifications.md for the resource model, lifecycle, HTTP API,
authorization, and the sensitive-data boundary.
Where does durable state live?
AppPort Services stores all state directly in FeltDB collections through the host's @feltdb/core (0.16.1 or later 0.x).
feltdb.flow (generated Flow contract)
│
▼
AppPort Services
│
├─→ FeltDbApiKeyStore (ApiKeys, ApiKeyPrefixes, ApiKeyAuditEvents)
├─→ FeltDbWebhookEndpointStore (WebhookEndpoints, WebhookAuditEvents)
├─→ FeltDbWebhookDeliveryStore (WebhookDeliveries)
├─→ FeltDbJobStore (Jobs, JobSchedules, JobAuditEvents)
└─→ Secrets protocol (Secrets, SecretVersions, SecretAuditEvents)
│
▼
@feltdb/core (the host's copy)
│
▼
real FeltDB
What happens to the secret?
For AppPort Secrets, the boundary is:
application .flow → capability contract → AuthBoundry authorization → secret provider
Applications declare the secrets capability, but never put secret values in .flow. AppPort publishes identity, lifecycle, tenant, audit, and provider-reference metadata only; AppBoundry supplies provider execution after AuthBoundry authorization.
AppPort is declarative: it defines what the Secrets capability means. AppBoundry decides how it runs, and AuthBoundry decides who may use it. The Secrets contract contains no provider, storage, caching, injection, or authorization implementation.
- generated with cryptographic randomness
- returned exactly once at creation time
- hashed with scrypt before persistence
- never stored in the durable API-key record
- excluded from audit records
Repository layout
appport.flow— Internal package template used to generate consumerfeltdb.flowcontracts/src/api-keys— API-key models and semantic service/src/authority— Policy Enforcement Point: capability manifest, verified principals, execution contexts, AuthBoundry protocol, destination policy, effect evidence/src/webhooks— Webhook models, service, and signing helpersmodels.ts— Endpoint, delivery, event contractsservice.ts— Webhook lifecycle and delivery orchestrationsecrets.ts— Secret generation, encryption, HMAC signing
/src/jobs— Job models, service, and executionmodels.ts— Job and schedule contractsservice.ts— Job lifecycle, retry, and schedulingstore.ts— FeltDB-backed storage with optimistic lockingworker.ts— Concurrent job execution with polling
/src/storage— FeltDB-backed store and audit sinkapi-keys.ts— API key storagewebhooks.ts— Webhook endpoint/delivery storage
/src/runtime— HTTP/Express authentication adapters and Bearer-token extractionapi-keys.ts— Bearer token extractionhttp-adapter.ts— Framework-neutral HTTP adapterexpress-middleware.ts— Express middleware
/src/contract— AuthPort-facing principal contract/tests— Node/TypeScript testscontract.test.ts— Flow DSL validation and collection verificationapi-keys.test.ts— Core service testshttp-adapter.test.ts— HTTP adapter tests (security, isolation, tenant safety)integration-app.test.ts— Real HTTP application fixtureexpress-integration.test.ts— Express middleware integration testswebhooks.test.ts— Webhook lifecycle and durability testswebhooks-delivery.test.ts— HTTP delivery, signing, retry, and concurrency testsjobs.test.ts— Job lifecycle, scheduling, and durability testsjobs-execution.test.ts— Concurrent execution, leasing, and recovery tests
/docs— architecture notes