Async gateway for worker-based services

Turn any service, anywhere, into a web request worker.

ServiceBridge is a job relay for asynchronous, worker-based systems. It exposes controlled HTTP routes, validates access, persists each request as a job, and lets a worker pick it up over outbound HTTPS from a laptop, container, VM, Kubernetes cluster, serverless runtime, or private network.

Supports async request/reply pull-based workers queue-backed routes local-to-cloud deploys
Request relay model
Browser
Webhook
Backend
ServiceBridge Route + policy + queue
Local worker
Cloud worker
Private service
1. RouteExpose controlled HTTP methods without moving your service.
2. QueuePersist accepted requests as jobs with leases, retries, and results.
3. PullWorkers poll outbound, execute local code, and report completion.
RQ

Route-to-job relay

Define public, authenticated, or internal routes. ServiceBridge turns allowed requests into jobs your workers can claim.

PL

Policy before execution

Apply method access rules, caller types, origins, service keys, user JWTs, rate limits, and tenant boundaries before a job exists.

ENV

Run workers anywhere

Keep services on localhost, in a private subnet, in containers, or across clouds. Workers only need outbound access to ServiceBridge.

DEV

Built for fast builders

Ideal for prototype developers and vibe-coded services: keep a real remote route online while the worker still runs on your machine.

Where it fits

For teams that want route control without surrendering hosting control.

ServiceBridge fits startups, prototype teams, AI agent backends, internal tools, and mid-scale applications that need route ownership while services remain portable. Use it when synchronous hosting is too rigid, tunnels are too fragile, or direct public ingress is not the architecture you want.

Async request/replyAccept a web request now, process it as a job, then return or stream the result when the worker completes.
Pull-based workersWorkers poll outbound for jobs, which works well for private networks, laptops, containers, and cloud services.
Leases, retries, and resultsEach job can be claimed, heartbeated, completed, failed, retried, or inspected from the control plane.
Control plane + data planeDefine routes, methods, access rules, keys, tenants, credits, and telemetry separately from worker runtime code.
Local-first development

Code locally, receive remote traffic, deploy when ready.

ServiceBridge gives early builders a full remote-coding path: publish the route once, let real clients or webhooks reach it, keep iterating on the worker locally, then move the same handler to hosted infrastructure without changing the client URL.

01 Publish route Expose a controlled HTTP endpoint through ServiceBridge.
02 Run local worker Code on your machine while jobs arrive over outbound polling.
03 Deploy worker Move the same handler to Docker, a VM, or Kubernetes.

ServiceBridge

Welcome back
SuperAdmin Overview Platform-wide users, tenants, credits, Stripe, and telemetry.
Balance$0 credits

Dashboard

This version implements the corrected hierarchy: Service → Resource → Method → Access Rules. A single method can now have different rules for public clients, authenticated frontend users, and backend service APIs.

0namespaces
0method + caller policies
0strict limits
0review required

Access Rule Evaluation Order

Runtime policy engine
1. Resolve slug routing

Reject if the service has no public slug route or is paused.

2. Resolve resource and method

Find the resource path and HTTP method.

3. Detect caller type

public_client, frontend_user, service_api, or worker.

4. Match access rule

Select rule by path + method + caller type + priority.

5. Enforce origin/IP/rate limits

Then create the async job if allowed.

Live Simulation Result

Latest
Run a simulation to see rule selection.

Playground

Create one Service and Resource, send mock POST and GET requests from a Client UI, and watch a Worker poll and process the resource live.

Initial Service and Resource Setup

new-user walkthrough
Client route /s/hello-api/hello Not created

Client UI

POST, poll, then GET the resource list
Create the setup, then POST or GET the resource.

Worker

waiting for setup
0
0
0
Worker will poll every few seconds after setup.

API Documentation

Complete reference for ServiceBridge: control plane APIs, data-plane invocation, method access rules, keys, rate limits, network controls, workers, jobs, alerts, billing, errors, and SDK usage.

What ServiceBridge Does

Platform overview

ServiceBridge is a communication layer for worker-based services. It turns client, backend, and internal-service requests into authorized jobs, routes them to the right worker fleet, tracks leases, events, and results, and gives operators control-plane and IaC surfaces for the workflow. Keeping services off public ingress is one benefit of the worker-polling model.

Client / Backend / Service
    ↓
ServiceBridge Communication Layer
    ↓
Policy + Routing
    ↓
Job Queue + Lease
    ↓
Worker Service
    ↓
Events / Results

Core Hierarchy

Correct model
Tenant
  └── Service
        └── Resource
              └── HTTP Method
                    └── Access Rules
                          ├── public_client rule
                          ├── frontend_user rule
                          ├── service_api rule
                          └── worker/admin rule

Important Design Rule

Do not mess this up
Enabling a service slug only means /s/{slug}/... exists. It does not mean every endpoint is public. Actual authorization is controlled by Method Access Rules.

Quickstart: Create service → resource → rule → invoke

POST
1. Create a service with slug routing enabled
curl -X POST https://api.servicebridge.com/v1/services \
  -H "Authorization: Bearer tenant_admin_token" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Hello API",
    "slug": "hello-api",
    "slugEnabled": true
  }'
2. Create the resource and methods
curl -X POST https://api.servicebridge.com/v1/resources \
  -H "Authorization: Bearer tenant_admin_token" \
  -H "Content-Type: application/json" \
  -d '{
    "service": "Hello API",
    "path": "/hello",
    "methods": ["GET", "POST"],
    "status": "Enabled"
  }'
3. Add client access rules
curl -X POST https://api.servicebridge.com/v1/access-rules \
  -H "Authorization: Bearer tenant_admin_token" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "name": "Public Hello Read",
      "service": "Hello API",
      "path": "/hello",
      "method": "GET",
      "callerType": "public_client",
      "authMode": "public_client_token",
      "ratePolicy": "generic_public_read_strict"
    },
    {
      "name": "Public Hello Create",
      "service": "Hello API",
      "path": "/hello",
      "method": "POST",
      "callerType": "public_client",
      "authMode": "public_client_token",
      "ratePolicy": "generic_public_write_strict"
    }
  ]'
4. Client calls GET first and receives a pending job for an empty list
curl -X GET https://api.servicebridge.com/s/hello-api/hello \
  -H "X-ServiceBridge-Client-Key: pk_live_hello_web"

202 Accepted
{
  "job_id": "job_get_001",
  "status": "pending",
  "result_url": "/v1/jobs/job_get_001"
}
5. Client calls POST with body params
curl -X POST https://api.servicebridge.com/s/hello-api/hello \
  -H "X-ServiceBridge-Client-Key: pk_live_hello_web" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "hello world",
    "createdBy": "new-user"
  }'

202 Accepted
{
  "job_id": "job_post_002",
  "status": "pending",
  "result_url": "/v1/jobs/job_post_002"
}
6. Worker polls the service and sees pending jobs
curl -X POST https://api.servicebridge.com/v1/services/svc_hello_api/jobs/claim \
  -H "Authorization: Bearer wk_live_hello_worker" \
  -H "Content-Type: application/json" \
  -d '{
    "worker_id": "hello-worker-1",
    "limit": 5,
    "lease_seconds": 60
  }'

200 OK
{
  "jobs": [
    { "id": "job_get_001", "method": "GET", "path": "/hello", "leaseToken": "lease_get" },
    { "id": "job_post_002", "method": "POST", "path": "/hello", "payload": { "message": "hello world" }, "leaseToken": "lease_post" }
  ]
}
7. Worker posts job results
curl -X POST https://api.servicebridge.com/v1/jobs/job_post_002/complete \
  -H "Authorization: Bearer wk_live_hello_worker" \
  -H "X-ServiceBridge-Lease-Token: lease_post" \
  -H "Content-Type: application/json" \
  -d '{
    "status": 201,
    "body": {
      "id": "hello_001",
      "message": "hello world",
      "createdBy": "new-user"
    }
  }'

curl -X POST https://api.servicebridge.com/v1/jobs/job_get_001/complete \
  -H "Authorization: Bearer wk_live_hello_worker" \
  -H "X-ServiceBridge-Lease-Token: lease_get" \
  -H "Content-Type: application/json" \
  -d '{
    "status": 200,
    "body": []
  }'
8. Client polls the job result, then GETs the full list
curl https://api.servicebridge.com/v1/jobs/job_post_002 \
  -H "X-ServiceBridge-Client-Key: pk_live_hello_web"

200 OK
{
  "id": "job_post_002",
  "status": "completed",
  "result": {
    "status": 201,
    "body": { "id": "hello_001", "message": "hello world" }
  }
}

curl -X GET https://api.servicebridge.com/s/hello-api/hello \
  -H "X-ServiceBridge-Client-Key: pk_live_hello_web"

202 Accepted
{ "job_id": "job_get_003", "status": "pending" }

// Worker completes job_get_003 with:
{ "status": 200, "body": [{ "id": "hello_001", "message": "hello world" }] }

Use Case Examples

Five-level implementation strategy

These demos are intentionally progressive. Start with one worker-backed request path, then add authentication, internal jobs, multi-service orchestration, and finally production-grade GitOps automation. The examples use a realistic e-commerce/bookstore business because it exercises public reads, logged-in writes, internal jobs, payment, inventory, notification, PDF generation, and CI/CD provisioning.

Level 1: Public Product Catalog API

GET

Need: send catalog read requests through a worker-backed service with identity, policy, queueing, and result handling. Anyone using the frontend can browse products, while the database and worker service stay protected by the outbound polling model.

Scenario:
  Public website lists books/products.

Services:
  Book Store: Slug Enabled

Resource:
  GET /book

Caller:
  public_client

Security:
  public client key
  origin allowlist
  strict public rate limit

Worker:
  Bookstore worker polls ServiceBridge and calls local bookstore code.
Access rule
{
  "name": "Public Catalog Read",
  "service_id": "svc_bookstore",
  "resource_path": "/book",
  "method": "GET",
  "caller_type": "public_client",
  "auth_mode": "public_client_token",
  "rate_limit_policy": "public_get_strict",
  "allowed_origins": ["https://bookstore.com"]
}
Client request
GET /s/bookstore/book?search=architecture
X-ServiceBridge-Client-Key: pk_live_bookstore_web
What ServiceBridge does
1. Resolve /s/bookstore/book
2. Match GET /book + public_client rule
3. Check origin and public client key
4. Apply rate limit by public_client_key_id + IP + origin + resource + method
5. Create Book Store job
6. Bookstore worker claims job and returns product list

Level 2: Authenticated Customer Actions with IAM JWT

POST

Need: logged-in users can create carts, save wishlists, or place orders. ServiceBridge validates the user JWT from the IAM provider before creating the job.

Scenario:
  Customer logs in through Auth0, Cognito, Keycloak, Okta, Clerk, Firebase Auth, or custom IAM.
  Frontend receives a JWT.
  Frontend calls ServiceBridge with public client key + JWT.

Services:
  Book Store: Slug Enabled

Resources:
  GET /book       public
  POST /cart      authenticated user
  POST /order     authenticated user

Caller:
  frontend_user

Security:
  public client key
  user JWT
  IAM provider config
  required scopes
  user-level rate limit
IAM provider
{
  "name": "bookstore-auth",
  "issuer": "https://auth.bookstore.com/",
  "jwks_uri": "https://auth.bookstore.com/.well-known/jwks.json",
  "audiences": ["bookstore-api"],
  "allowed_algorithms": ["RS256"],
  "claim_mapping": {
    "subject": "sub",
    "scope": "scope",
    "roles": "https://bookstore.com/roles",
    "email": "email"
  }
}
Access rule for authenticated cart creation
{
  "name": "Customer Create Cart",
  "resource_path": "/cart",
  "method": "POST",
  "caller_type": "frontend_user",
  "auth_mode": "public_client_plus_user_token",
  "jwt_provider_id": "iam_bookstore_auth",
  "required_scopes": ["cart:create"],
  "rate_limit_policy": "frontend_write_strict"
}
Client request
POST /s/bookstore/cart
X-ServiceBridge-Client-Key: pk_live_bookstore_web
Authorization: Bearer user_jwt

{
  "items": [
    { "sku": "book_001", "quantity": 1 }
  ]
}
Job auth_context
{
  "caller_type": "frontend_user",
  "jwt_provider_id": "iam_bookstore_auth",
  "user": {
    "sub": "user_456",
    "issuer": "https://auth.bookstore.com/",
    "audience": "bookstore-api",
    "scopes": ["cart:create", "order:create"],
    "email": "customer@example.com"
  },
  "matched_access_rule_id": "rule_customer_create_cart"
}

Level 3: Internal Job Only PDF Generation

POST

Need: after an order is confirmed, generate an invoice PDF. The PDF Generator has no public route and does not need a stable public IP. It receives jobs through ServiceBridge.

Scenario:
  Customer places order.
  Bookstore worker creates order.
  Bookstore worker enqueues internal PDF job.
  PDF worker claims PDF job from another machine.

Services:
  Book Store: Slug Enabled
  PDF Generator: Internal Job Only

Important:
  PDF Generator has no /s/pdf-generator/... public URL.
  Workers communicate through ServiceBridge, not directly.
Internal job permission
{
  "source_service_id": "svc_bookstore",
  "target_service_id": "svc_pdf_generator",
  "operation": "generate_invoice_pdf",
  "required_scopes": ["pdf:generate"],
  "rate_limit_policy": "internal_pdf_generation",
  "max_pending_jobs": 5000
}
Bookstore worker enqueues PDF job
POST /v1/internal/jobs
Authorization: Bearer sk_live_bookstore_service
Idempotency-Key: invoice:order_789

{
  "target_service_id": "svc_pdf_generator",
  "operation": "generate_invoice_pdf",
  "payload": {
    "order_id": "order_789",
    "customer_id": "cust_456",
    "template": "invoice_v1"
  }
}
PDF worker claims job
POST /v1/services/svc_pdf_generator/jobs/claim
Authorization: Bearer wk_live_pdf_worker

{
  "worker_id": "pdf-worker-1",
  "limit": 5,
  "lease_seconds": 60
}
Same tenant is required, but same tenant is not enough. The source service must have an explicit source→target→operation permission.

Level 4: Multi-Service E-Commerce Order Workflow

POST

Need: a real order requires inventory reservation, payment authorization, invoice generation, and notification. Some services are slug-enabled, some are internal job-only, and some are internal M2M.

Scenario:
  Customer places order from frontend.
  Book Store creates order.
  Inventory reserves stock.
  Payment authorizes charge.
  PDF Generator creates invoice.
  Notification sends email.

Services:
  Book Store: Slug Enabled
  Inventory Service: Internal Job Only
  Payment Service: Internal M2M Data Plane or Internal Job Only
  PDF Generator: Internal Job Only
  Notification Service: Internal Job Only
Internal permissions
[
  {
    "source_service_id": "svc_bookstore",
    "target_service_id": "svc_inventory",
    "operation": "reserve_stock",
    "required_scopes": ["inventory:reserve"]
  },
  {
    "source_service_id": "svc_bookstore",
    "target_service_id": "svc_payment",
    "operation": "authorize_payment",
    "required_scopes": ["payment:authorize"]
  },
  {
    "source_service_id": "svc_bookstore",
    "target_service_id": "svc_pdf_generator",
    "operation": "generate_invoice_pdf",
    "required_scopes": ["pdf:generate"]
  },
  {
    "source_service_id": "svc_bookstore",
    "target_service_id": "svc_notification",
    "operation": "send_order_confirmation",
    "required_scopes": ["notification:send"]
  }
]
Workflow shape
POST /s/bookstore/order
  → Bookstore job

Bookstore worker:
  → enqueue reserve_stock
  → enqueue authorize_payment
  → create order
  → enqueue generate_invoice_pdf
  → enqueue send_order_confirmation

Internal workers:
  Inventory worker claims reserve_stock
  Payment worker claims authorize_payment
  PDF worker claims generate_invoice_pdf
  Notification worker claims send_order_confirmation
This is the best strategic demo because it shows ServiceBridge as more than an API proxy: it becomes a secure async orchestration layer across private services.

Level 5: Production GitOps Rollout Across Environments

CLI

Need: provision the entire e-commerce integration consistently across dev, staging, and production using YAML, CLI, CI/CD, tenant automation keys, approvals, and drift detection.

Scenario:
  Engineering team manages ServiceBridge configuration in Git.
  Pull request changes servicebridge.yaml.
  CI validates and plans.
  Production apply happens after approval.

Products used:
  ServiceBridge API
  SDK
  CLI / IaC
  Tenant Automation Keys
  IAM / JWT Providers
  Internal Job Permissions
  Rate Limits
  Alerts
  Network / DDoS Controls
GitHub Actions rollout
name: ServiceBridge Production Apply

on:
  push:
    branches: [main]
    paths:
      - servicebridge.yaml

jobs:
  apply:
    runs-on: ubuntu-latest
    environment: production

    steps:
      - uses: actions/checkout@v4

      - run: npm install -g @servicebridge/cli

      - run: sbctl validate -f servicebridge.yaml
        env:
          SERVICEBRIDGE_TENANT_KEY: ${{ secrets.SERVICEBRIDGE_TENANT_KEY }}

      - run: sbctl plan -f servicebridge.yaml --env prod
        env:
          SERVICEBRIDGE_TENANT_KEY: ${{ secrets.SERVICEBRIDGE_TENANT_KEY }}

      - run: sbctl apply -f servicebridge.yaml --env prod --non-interactive
        env:
          SERVICEBRIDGE_TENANT_KEY: ${{ secrets.SERVICEBRIDGE_TENANT_KEY }}
Production controls
tenant automation key:
  scopes:
    - tenant:read
    - services:write
    - resources:write
    - access_rules:write
    - rate_limits:write
    - iam_providers:write
    - internal_permissions:write
    - network:write
    - alerts:write
  allowed_ips:
    - 203.0.113.20/32
  expires_at: 2027-01-01

pipeline:
  validate
  plan
  approval
  apply
  audit
  drift detection

Bonus Scenario: AI Chatbot with Agent Tool Workers

AI

Need: logged-in users send prompts, Agent workers claim prompt jobs, and internal tool workers perform RAG, web search, MCP/product search, memory lookup, and LLM inference. The response can stream partial chunks and store the final answer.

Services:
  AI Chat API: Slug Enabled
  Agent Orchestrator: Internal Job Only
  RAG Retrieval Service: Internal Job Only
  Web Search Tool: Internal Job Only
  MCP Product Search: Internal Job Only
  Memory Service: Internal Job Only
  LLM Inference Service: Internal Job Only

Slug route:
  POST /s/ai-chat/chat

Result channel:
  /v1/jobs/{job_id}/events

Internal permissions:
  AI Chat API → Agent Orchestrator.run_agent_turn
  Agent Orchestrator → RAG.retrieve_context
  Agent Orchestrator → Web Search.web_search
  Agent Orchestrator → MCP Product Search.product_search
  Agent Orchestrator → Memory.get_user_memory
  Agent Orchestrator → LLM.generate_response

Playground

Hands-on Level 1 flow

The Playground is a guided first-run test for the core worker-service communication path. It creates one slug-enabled service, one resource, public GET/POST rules, a mock client, and a mock worker. Use it to verify the basic job lifecycle before moving into the deeper use-case scenarios.

Scope: this playground currently covers Level 1 only. The five-level use-case documentation remains the source of truth for authenticated customer actions, internal PDF generation, multi-service e-commerce orchestration, and production GitOps rollout.
Setup:
  Service: Hello API
  Resource: /hello
  Methods: POST, GET

ClientUI:
  POST /s/hello-api/hello
  Poll job until completed
  GET /s/hello-api/hello

Worker:
  Polls pending jobs
  Creates a Hello world object for POST
  Returns stored objects for GET

How It Maps to the Use-Case Levels

GUIDE
Level
Playground Coverage
Where to Continue
Level 1
Covered: slug-enabled route, resource, access rules, client job creation, worker claim/complete, result polling.
Use the Playground and the Overview quickstart.
Level 2
Not simulated: IAM provider, JWT claims, protected frontend user writes.
Use the Auth and IAM/JWT docs.
Level 3
Not simulated: internal PDF Generator Worker and source-to-target permission.
Use Internal Job Auth and Worker Plane docs.
Level 4
Not simulated: inventory, payment, PDF, notification, and multiple worker services.
Use the Use Case Examples and SDK Bookstore Demo.
Level 5
Not simulated: YAML plan/apply, tenant automation keys, and CI/CD promotion.
Use CLI Documentation and YAML Configuration.

Authentication Types

Caller identity
Type
Header
Use Case
Public Client Key
X-ServiceBridge-Client-Key
Frontend/browser app identification. Copyable, not secret.
User JWT
Authorization: Bearer user_jwt
Logged-in user identity and scopes.
Service API Key
Authorization: Bearer sk_live_xxx
Backend-to-backend machine identity.
Worker Key
Authorization: Bearer wk_live_xxx
Private worker claiming/completing jobs.

Generate Public Client Key

POST

Public client keys are safe to embed in frontend code, but they are not secrets. Use them for app identity, origin checks, and rate limits.

POST /v1/services/{service_id}/keys/public-client

{
  "name": "Bookstore Web",
  "allowed_origins": ["https://bookstore.com"],
  "scope": ["bookstore:GET:/book"],
  "rate_limit_policy": "public_get_strict"
}

Generate Service API Key

POST

Service API keys are secret. Show once, store hash only. Use for backend-to-backend calls.

POST /v1/services/{service_id}/keys/service-api

{
  "name": "Inventory Backend",
  "scopes": ["book:read", "book:create"],
  "allowed_ips": ["18.141.10.22/32"],
  "expires_at": "2027-01-01T00:00:00Z"
}
Do not use public client keys as real security. They identify an app, not a trusted user. For protected frontend actions, require both public client key and user JWT.

Configuring JWT Authentication with Any IAM Provider

OIDC / JWKS compatible

ServiceBridge validates user JWTs at ingress for routes using auth_mode = public_client_plus_user_token or user_token. The backend may still revalidate for defense-in-depth, but the authorization decision should happen before job creation.

Supported model: any IAM that can issue JWTs and expose OIDC discovery or JWKS, including Auth0, Cognito, Okta, Azure AD, Keycloak, Clerk, Firebase Auth, or a custom IAM server.
For local testing, use the IAM / JWT Providers page to create a Mock ServiceBridge JWT Provider, generate a local mock token, and validate issuer, audience, scopes, subject, and claim mapping without connecting a real IAM.

1. Create IAM Provider

POST

Configure issuer, audience, JWKS, accepted algorithms, and claim mapping.

POST /v1/iam-providers
Authorization: Bearer tenant_admin_token
Content-Type: application/json

{
  "name": "Bookstore Auth0",
  "provider_type": "oidc",
  "issuer": "https://auth.bookstore.com/",
  "discovery_url": "https://auth.bookstore.com/.well-known/openid-configuration",
  "jwks_uri": "https://auth.bookstore.com/.well-known/jwks.json",
  "audiences": ["bookstore-api"],
  "allowed_algorithms": ["RS256"],
  "claim_mapping": {
    "subject": "sub",
    "scope": "scope",
    "roles": "https://bookstore.com/roles",
    "tenant": "https://bookstore.com/tenant_id",
    "email": "email"
  },
  "jwks_cache_seconds": 3600,
  "clock_skew_seconds": 60,
  "status": "active"
}

2. Attach IAM Provider to Access Rule

POST

The access rule tells ServiceBridge which provider to use and what scopes or roles are required.

POST /v1/access-rules

{
  "name": "Frontend Book Create",
  "service_id": "svc_bookstore",
  "resource_path": "/book",
  "method": "POST",
  "caller_type": "frontend_user",
  "auth_mode": "public_client_plus_user_token",
  "jwt_provider_id": "iam_auth0_bookstore",
  "required_scopes": ["book:create"],
  "required_roles": ["seller"],
  "rate_limit_policy": "frontend_write_strict",
  "allowed_origins": ["https://bookstore.com"]
}

3. Frontend Calls ServiceBridge with Public Key + JWT

POST
POST /s/bookstore/book
X-ServiceBridge-Client-Key: pk_live_xxx
Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImtpZF8yMDI2X3ByaW1hcnkifQ...

{
  "title": "The Art of War"
}

JWT Validation Algorithm

Data plane execution
1. Parse Authorization Bearer token
2. Decode JWT header
3. Reject unsupported alg
4. Resolve IAM provider from access rule
5. Fetch/cache JWKS
6. Find public key by kid
7. Verify signature
8. Verify iss exactly equals configured issuer
9. Verify aud contains configured audience
10. Verify exp, nbf, iat with clock skew
11. Extract subject, scopes, roles, tenant claim
12. Check tenant claim if configured
13. Check required scopes/roles from access rule
14. Create normalized auth_context
15. Create job

4. Job Contains Normalized auth_context

JOB
{
  "job_id": "job_123",
  "target_service_id": "svc_bookstore",
  "operation": "POST /book",
  "authorized_at": "2026-07-04T12:00:00Z",
  "auth_context": {
    "caller_type": "frontend_user",
    "public_client_key_id": "key_public_123",
    "jwt_provider_id": "iam_auth0_bookstore",
    "user": {
      "sub": "user_456",
      "issuer": "https://auth.bookstore.com/",
      "audience": "bookstore-api",
      "scopes": ["book:create", "book:read"],
      "roles": ["seller"],
      "email": "lucas@example.com"
    },
    "matched_access_rule_id": "rule_frontend_book_create"
  }
}
In async systems, validate the JWT when the request enters ServiceBridge. A JWT may expire before a worker processes the job. The backend can optionally revalidate, but the ingress decision should be preserved as auth_context.

Test IAM Provider Config

POST
POST /v1/iam-providers/{provider_id}/test-token

{
  "jwt": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImtpZF8yMDI2X3ByaW1hcnkifQ..."
}
{
  "valid": true,
  "subject": "user_456",
  "issuer": "https://auth.bookstore.com/",
  "audience": "bookstore-api",
  "scopes": ["book:create", "book:read"],
  "roles": ["seller"],
  "key_id": "kid_2026_primary"
}
Validation Failure
HTTP
Meaning
JWT_PROVIDER_NOT_CONFIGURED
401
No provider is attached to the access rule.
JWT_SIGNATURE_INVALID
401
Signature cannot be verified against JWKS.
JWT_ISSUER_MISMATCH
401
iss does not match configured issuer.
JWT_AUDIENCE_MISMATCH
401
aud does not contain required audience.
JWT_EXPIRED
401
exp is in the past beyond allowed skew.
JWT_SCOPE_DENIED
403
Token is valid but lacks required scopes or roles.

Internal Service Authentication & Job Enforcement

source service → target service

Internal services must never be callable by every service automatically. ServiceBridge derives the source service and tenant from the service API key, then checks explicit Internal Job Permissions before creating a job.

Do not trust source_service_id from the request body. It must be derived from the authenticated service key.

1. Service API Key Binds Caller to Tenant and Source Service

KEY
{
  "key_id": "key_bookstore_service",
  "key_type": "service_api",
  "tenant_id": "tenant_123",
  "service_id": "svc_bookstore",
  "scopes": ["order:create", "pdf:generate"],
  "status": "active"
}

When this key calls POST /v1/internal/jobs, ServiceBridge derives tenant_123 and svc_bookstore from the key.

2. Create Internal Job Permission

POST
POST /v1/internal-job-permissions
Authorization: Bearer tenant_admin_token

{
  "tenant_id": "tenant_123",
  "source_service_id": "svc_bookstore",
  "target_service_id": "svc_pdf_generator",
  "operation": "generate_invoice_pdf",
  "required_scopes": ["pdf:generate"],
  "rate_limit_policy": "internal_pdf_generation",
  "max_pending_jobs": 5000,
  "status": "active"
}

This allows Book Store to enqueue only the generate_invoice_pdf operation on PDF Generator. It does not grant access to every operation.

3. Enqueue Internal Job

POST
POST /v1/internal/jobs
Authorization: Bearer sk_live_bookstore_service

{
  "target_service_id": "svc_pdf_generator",
  "operation": "generate_invoice_pdf",
  "payload": {
    "order_id": "order_789",
    "customer_id": "cust_456",
    "template": "invoice_v1"
  }
}

The body contains target service and operation. It does not contain source service because ServiceBridge derives source from the key.

Internal Job Authorization Algorithm

Enforcement order
1. Validate service API key
2. Derive caller.tenant_id from key
3. Derive source_service_id from key
4. Read target_service_id and operation from request body
5. Load target service
6. Reject if target.tenant_id != caller.tenant_id
7. Reject if target slug mode does not accept internal jobs
8. Find active permission:
   tenant_id + source_service_id + target_service_id + operation
9. Reject if permission missing
10. Check key scopes include permission.required_scopes
11. Apply rate limit:
    tenant_id + source_service_id + target_service_id + operation
12. Check target max pending jobs
13. Create job with source_service_id and target_service_id

Allowed Result

202
{
  "job_id": "job_pdf_001",
  "status": "pending",
  "tenant_id": "tenant_123",
  "source_service_id": "svc_bookstore",
  "target_service_id": "svc_pdf_generator",
  "operation": "generate_invoice_pdf"
}
Denied Case
Error Code
Meaning
Different tenant
CROSS_TENANT_INTERNAL_JOB_DENIED
Source key tenant does not match target service tenant.
No permission
INTERNAL_JOB_PERMISSION_DENIED
No explicit source→target→operation permission exists.
Missing scope
INTERNAL_JOB_SCOPE_DENIED
Service key lacks the required operation scope.
Target not internal-job capable
TARGET_SERVICE_NOT_INTERNAL_JOB_ENABLED
Target service does not accept internal jobs.
Queue full
TARGET_QUEUE_QUOTA_EXCEEDED
Target pending jobs exceed configured limit.
Clean rule: a service can enqueue an internal job only if it is the same tenant, has an explicit source→target permission, the operation is allowed, the key has required scopes, and rate/queue limits pass.

Method Access Rules

Most important concept

Access rules allow the same route and method to have different auth and rate limits depending on caller type.

GET /book + public_client → public_get_strict
GET /book + service_api   → m2m_get_high
POST /book + frontend_user → frontend_write_strict
DELETE /book + service_api → admin_destructive_strict

Worker Pool Behavior

Service queue distribution

A service can have one worker or many workers. Access rules only decide whether a caller is allowed to create a job; they do not assign that job to a specific worker.

Service: Book Store
Workers: bookstore-worker-a, bookstore-worker-b, bookstore-worker-c

1. Client request passes the access rule.
2. ServiceBridge creates one pending job for the Book Store service.
3. Any active Book Store worker may poll and claim the job.
4. The first eligible worker receives a lease token for that job.
5. Other workers cannot complete that same job without the lease token.

If a worker fails, stops heartbeating, or never returns a result, the lease eventually expires. The job can then be retried, claimed by another worker in the same service pool, or moved to a failed/dead-letter state after the retry policy is exhausted.

Create Access Rule

POST
POST /v1/access-rules

{
  "name": "Public Book Read",
  "service_id": "svc_bookstore",
  "resource_path": "/book",
  "method": "GET",
  "caller_type": "public_client",
  "auth_mode": "public_client_token",
  "rate_limit_policy": "public_get_strict",
  "required_scopes": [],
  "allowed_origins": ["https://bookstore.com"],
  "allowed_ips": [],
  "blocked_ips": [],
  "priority": 100,
  "status": "active"
}

Backend Rule for Same GET /book

POST
{
  "name": "Backend Book Read",
  "service_id": "svc_bookstore",
  "resource_path": "/book",
  "method": "GET",
  "caller_type": "service_api",
  "auth_mode": "service_token",
  "rate_limit_policy": "m2m_get_high",
  "required_scopes": ["book:read"],
  "allowed_ips": ["18.141.10.22/32"],
  "priority": 200
}
Field
Type
Meaning
caller_type
enum
public_client, frontend_user, service_api, worker, anonymous.
auth_mode
enum
Credential requirement for the matched rule.
rate_limit_policy
string
Rate policy applied only to this matched rule.
priority
number
Used if several rules could match.

Rate Limiting

Per access rule

Rate limits attach to access rules, not just resources. This lets public GET and backend GET have different limits.

Create Rate Limit Policy

POST
POST /v1/rate-limit-policies

{
  "name": "public_get_strict",
  "limit": 120,
  "window": "1m",
  "burst": 20,
  "algorithm": "sliding_window",
  "dimensions": [
    "public_client_key_id",
    "ip_address",
    "origin",
    "resource_id",
    "method"
  ]
}

Public Client Dimensions

Strict
[
  "public_client_key_id",
  "ip_address",
  "origin",
  "resource_id",
  "method"
]

Service API Dimensions

Looser
[
  "service_key_id",
  "service_id",
  "resource_id",
  "method"
]

Control Plane APIs

Admin/configuration

Control Plane APIs create and manage services, resources, access rules, rate limits, keys, network controls, alerts, and tenant settings.

Services

POST
POST /v1/services
GET /v1/services
GET /v1/services/{service_id}
PATCH /v1/services/{service_id}
DELETE /v1/services/{service_id}

Resources

POST
POST /v1/services/{service_id}/resources
GET /v1/services/{service_id}/resources
PATCH /v1/resources/{resource_id}
DELETE /v1/resources/{resource_id}

Access Rules

POST
POST /v1/access-rules
GET /v1/access-rules?service_id=svc_bookstore
GET /v1/access-rules/{rule_id}
PATCH /v1/access-rules/{rule_id}
DELETE /v1/access-rules/{rule_id}

Keys

POST
POST /v1/services/{service_id}/keys/public-client
POST /v1/services/{service_id}/keys/service-api
POST /v1/services/{service_id}/keys/worker
GET /v1/keys
POST /v1/keys/{key_id}/rotate
POST /v1/keys/{key_id}/revoke

Data Plane Invocation

Client traffic

The data plane receives public or backend traffic, evaluates access rules, creates jobs, and returns a job ID.

Invoke Any Resource

ANY
ANY /s/{service_slug}/{resource_path...}
GET /s/bookstore/book
X-ServiceBridge-Client-Key: pk_live_xxx
HTTP/1.1 202 Accepted

{
  "job_id": "job_123",
  "status": "pending",
  "result_url": "/v1/jobs/job_123"
}

Rule Evaluation

Runtime order
1. Resolve service by slug
2. Check service slug routing
3. Resolve resource path
4. Resolve HTTP method
5. Detect caller type
6. Match access rule
7. Validate credential and scopes
8. Check origin/IP allowlist/blocklist
9. Apply rate limit policy
10. Apply tenant quota
11. Create job

Worker Plane APIs

Private execution

Workers live near private services. They poll ServiceBridge, claim jobs, execute locally, and return results.

Claim Jobs

POST
POST /v1/services/{service_id}/jobs/claim
Authorization: Bearer wk_live_xxx

{
  "worker_id": "worker-book-1",
  "limit": 5,
  "lease_seconds": 60
}

Complete Job

POST
POST /v1/jobs/{job_id}/complete
Authorization: Bearer wk_live_xxx

{
  "status_code": 201,
  "headers": { "content-type": "application/json" },
  "body": { "book_id": "book_123" }
}

Fail Job

POST
POST /v1/jobs/{job_id}/fail

{
  "error_code": "PRIVATE_SERVICE_TIMEOUT",
  "message": "Book service did not respond",
  "retryable": true
}

Get Job Result

GET
GET /v1/jobs/{job_id}
{
  "job_id": "job_123",
  "status": "completed",
  "status_code": 201,
  "body": { "book_id": "book_123" },
  "trace_id": "trace_abc"
}

Job States

Lifecycle
pending
claimed
processing
completed
failed
expired
cancelled
dead_letter

Network & DDoS Controls

Defense in depth
Application rate limits are not enough for real DDoS protection. Put ServiceBridge behind a serious edge/WAF layer.

Update Tenant Network Policy

PATCH
PATCH /v1/tenant/network-policy

{
  "waf_mode": "challenge",
  "bot_protection": true,
  "tenant_denylist": ["203.0.113.10", "198.51.100.0/24"],
  "tenant_global_rpm": 10000
}
Control
Best For
Weakness
IP Allowlist
Service API keys with stable backend egress IPs.
Bad for dynamic local/dev/proxy environments.
IP Denylist
Known attackers and incident response.
Reactive; attackers rotate IPs.
Public Client Key
App identity and quotas.
Copyable from browser code.
WAF/Bot Protection
Edge-level abuse protection.
Needs tuning to avoid false positives.

Create Alert Rule

POST
POST /v1/alerts

{
  "severity": "critical",
  "signal": "DLQ jobs",
  "condition": "DLQ > 5 for 5m",
  "channel": "Slack #ops"
}

Recommended Alerts

Minimum viable ops
Queue backlog too high
DLQ jobs increasing
Worker heartbeat missing
p95 job completion latency breached
Public-client 429 rate spike
Service API key nearing expiry
Tenant quota nearing limit

Queue, Jobs, and Results Observability

operator workflow
Surface
What it answers
Operator action
Queue & Jobs
Which jobs are pending, claimed, completed, retried, failed, or in DLQ.
Inspect the selected job request, response, events, worker, and timestamps.
Result Channels
How callers receive final results, progress events, token chunks, webhooks, or stored responses.
Choose polling, SSE, websocket, webhook, or stored-result delivery with retention and auth.
Alerts
Which runtime signals should notify humans before users notice failures.
Create rules for backlog, stale workers, DLQ growth, stream failures, latency, and quota risk.

Inspect Job Runtime State

GET
GET /v1/jobs/{job_id}
{
  "job_id": "job_123",
  "status": "completed",
  "request": {
    "method": "POST",
    "route": "/s/ai-chat/chat",
    "headers": { "origin": "https://chat.example.com" },
    "body": { "message": "Summarize this order" }
  },
  "response": {
    "status_code": 200,
    "body": { "answer": "Order summary..." }
  },
  "events": [
    { "type": "job.accepted" },
    { "type": "job.claimed" },
    { "type": "result.chunk" },
    { "type": "job.completed" }
  ]
}

Subscribe to Result Events

GET
GET /v1/jobs/{job_id}/events
Accept: text/event-stream
event: result.chunk
data: {"sequence":42,"delta":"The next step is..."}

event: job.completed
data: {"status_code":200,"trace_id":"trace_ai_abc"}

Billing & Usage

credits and sandbox grants
Unit
Meaning
Example
Request
Accepted data-plane request.
GET /s/bookstore/book
Job
Async job created and stored.
job_123
Storage
Payload, response, logs, traces.
34GB retained
Protection
WAF, bot checks, advanced security.
challenge mode
Sandbox credit
Monthly expiring test grant for local experiments.
$10 through month end

Claim Monthly Sandbox Credit

POST
POST /api/billing/sandbox-credit/claim

{
  "tenantId": "tenant_123"
}

{
  "sandboxCredit": {
    "amount": 10,
    "claimMonth": "2026-07",
    "status": "active",
    "expiresAt": "2026-07-31T23:59:59.999Z"
  }
}

Usage API

GET
GET /v1/usage?from=2026-07-01&to=2026-07-31

{
  "requests": 1200000,
  "jobs": 890000,
  "storage_gb": 34,
  "estimated_total_usd": 53.20
}

Error Format

Consistent response model
{
  "error": {
    "code": "NO_MATCHING_ACCESS_RULE",
    "message": "No access rule matched GET /book for caller_type=anonymous",
    "request_id": "req_abc",
    "trace_id": "trace_abc"
  }
}
Code
HTTP
Meaning
SERVICE_NOT_FOUND
404
Service slug does not exist.
SERVICE_SLUG_DISABLED
403
Service has no enabled slug route.
NO_MATCHING_ACCESS_RULE
403
No rule for path + method + caller type.
ORIGIN_NOT_ALLOWED
403
Browser origin failed allowlist.
IP_NOT_ALLOWLISTED
403
Service API key IP restriction failed.
RATE_LIMITED
429
Matched rate policy was exceeded.
JWT_SIGNATURE_INVALID
401
User JWT failed signature validation.
JWT_SCOPE_DENIED
403
User JWT lacks required scope or role.
CROSS_TENANT_INTERNAL_JOB_DENIED
403
Source service key tenant does not match target service tenant.
INTERNAL_JOB_PERMISSION_DENIED
403
No explicit source→target→operation permission exists.

Webhook: Job Completed

POST
{
  "event": "job.completed",
  "job_id": "job_123",
  "trace_id": "trace_abc",
  "status_code": 201,
  "created_at": "2026-07-04T12:00:00Z"
}

ServiceBridge SDK Overview

Developer product

The ServiceBridge SDK helps developers build job-based integrations without manually implementing polling, leases, retries, completion calls, failure handling, heartbeats, and internal job dispatch. Developers focus on business functions; the SDK adapts those functions into ServiceBridge workers.

SDK goal: turn a normal local function into a reliable ServiceBridge job handler.
Without SDK:
  write polling loop
  claim jobs
  manage leases
  route operations
  call local functions
  serialize responses
  complete/fail jobs
  heartbeat workers
  retry safely
  handle shutdown

With SDK:
  bridge.on("POST /book", createBook)
  bridge.on("generate_invoice_pdf", generatePdf)
  bridge.start()

Developer SDK

Build worker job handlers quickly

The SDK lets developers register local functions as ServiceBridge job handlers. It handles polling, claiming, leases, heartbeats, retries, completion, failure, shutdown, and internal job dispatch.

import { ServiceBridgeWorker } from "@servicebridge/node";

const worker = new ServiceBridgeWorker({
  serviceId: "svc_bookstore",
  workerKey: process.env.WORKER_KEY
});

worker.on("POST /order", async (job, ctx) => {
  return createOrder(job.payload);
});

worker.start();

Worker SDK

Private services

Runs beside private services. Polls ServiceBridge, claims jobs, executes handlers, and completes or fails jobs.

Client SDK

Frontend/backend callers

Invokes ServiceBridge routes and waits for results. Handles job polling, timeout, cancellation, and result parsing.

Internal Jobs SDK

Service-to-service jobs

Lets a service enqueue internal jobs to allowed target services using Internal Job Permissions.

SDK Package Layout

SDK
@servicebridge/node
  ServiceBridgeClient       // invoke public slug routes and get job results
  ServiceBridgeWorker       // poll, claim, handle, complete jobs
  InternalJobClient         // enqueue internal jobs source→target
  createExpressAdapter      // optional Express integration
  createFastifyAdapter      // optional Fastify integration
  createNextRouteAdapter    // optional Next.js route integration
  types                     // Job, Context, AuthContext, Result, Error types

Install

NPM
npm install @servicebridge/node
pnpm add @servicebridge/node
yarn add @servicebridge/node
SDK Secret
Used By
Purpose
Public Client Key
Frontend Client SDK
Invoke public/frontend routes. Not secret.
User JWT
Frontend Client SDK
Authenticated user identity from IAM provider.
Service API Key
Backend Client / InternalJobClient
Backend-to-ServiceBridge calls and internal job enqueue.
Worker Key
Worker SDK
Claim, heartbeat, complete, and fail jobs for assigned service.

Worker SDK

Polling adapter for private services

The Worker SDK connects a private machine to ServiceBridge. It only needs outbound HTTPS. It claims jobs assigned to a service, calls local handlers, and reports results back to ServiceBridge.

Minimal Worker

CODE
import { ServiceBridgeWorker } from "@servicebridge/node";

const worker = new ServiceBridgeWorker({
  serviceId: process.env.SERVICEBRIDGE_SERVICE_ID,
  workerKey: process.env.SERVICEBRIDGE_WORKER_KEY,
  endpoint: "https://api.servicebridge.com",
  workerId: "bookstore-worker-1"
});

worker.on("POST /book", async (job, ctx) => {
  const book = await createBook(job.payload);

  return {
    statusCode: 201,
    body: book
  };
});

await worker.start();

Worker Config

CONFIG
const worker = new ServiceBridgeWorker({
  endpoint: "https://api.servicebridge.com",
  serviceId: "svc_bookstore",
  workerKey: "wk_live_xxx",

  workerId: "bookstore-worker-prod-1",
  concurrency: 10,
  pollIntervalMs: 1000,
  maxBatchSize: 5,
  leaseSeconds: 60,

  heartbeatIntervalMs: 15000,
  autoExtendLease: true,
  gracefulShutdownTimeoutMs: 30000,

  logLevel: "info"
});
Option
Type
Description
serviceId
string
Service whose queue this worker claims from.
workerKey
string
Worker key scoped to claim/complete/fail jobs for this service.
concurrency
number
Maximum jobs processed in parallel.
pollIntervalMs
number
How often to claim jobs when queue is empty.
maxBatchSize
number
How many jobs to claim per request.
leaseSeconds
number
How long the claim is valid before another worker can reclaim.
autoExtendLease
boolean
Automatically extends job lease while handler is running.
heartbeatIntervalMs
number
How often worker reports liveness.

What the SDK Does Internally

FLOW
while running:
  send heartbeat
  claim jobs from /v1/services/{service_id}/jobs/claim
  match each job to a registered handler
  create execution context
  run handler
  serialize handler result
  POST /v1/jobs/{job_id}/complete
  if handler throws:
    POST /v1/jobs/{job_id}/fail
  if shutdown:
    stop claiming new jobs
    finish or safely release active jobs

Job Handlers

Turn functions into ServiceBridge operations

Handlers map ServiceBridge operations to local code. A handler can represent a slug route such as POST /book or an internal operation such as generate_invoice_pdf.

Route Handler

CODE
worker.on("GET /book", async (job, ctx) => {
  const books = await bookstore.listBooks({
    limit: job.query.limit,
    search: job.query.search
  });

  return {
    statusCode: 200,
    body: { books }
  };
});

worker.on("POST /book", async (job, ctx) => {
  ctx.requireScope("book:create");

  const book = await bookstore.createBook({
    title: job.payload.title,
    author: job.payload.author,
    createdBy: ctx.auth.user?.sub
  });

  return {
    statusCode: 201,
    body: book
  };
});

Internal Operation Handler

CODE
worker.operation("generate_invoice_pdf", async (job, ctx) => {
  const pdf = await pdfGenerator.generateInvoice({
    orderId: job.payload.order_id,
    customerId: job.payload.customer_id,
    template: job.payload.template
  });

  return {
    body: {
      pdf_id: pdf.id,
      pdf_url: pdf.url
    }
  };
});
Handler Input
Type
Meaning
job.id
string
ServiceBridge job ID.
job.method
string
HTTP method for route jobs.
job.path
string
Resource path for route jobs.
job.operation
string
Operation name for internal job-only services.
job.payload
object
Request body or internal job payload.
job.query
object
Query parameters from slug-route invocation.
ctx.auth
AuthContext
Normalized auth decision from ServiceBridge.
ctx.traceId
string
Trace ID for logs and debugging.

Error Handling

CODE
import { RetryableError, PermanentError } from "@servicebridge/node";

worker.on("POST /book", async (job, ctx) => {
  try {
    return await createBook(job.payload);
  } catch (err) {
    if (err.code === "DB_TIMEOUT") {
      throw new RetryableError("Database timeout", {
        retryAfterSeconds: 30
      });
    }

    throw new PermanentError("Invalid book payload", {
      statusCode: 400,
      details: err.message
    });
  }
});

Internal Jobs SDK

Service-to-service async dispatch

The Internal Jobs SDK lets one service enqueue a job to another service without direct network access. ServiceBridge enforces same tenant, source service identity, explicit Internal Job Permission, scopes, rate limits, and queue quotas.

Bookstore Worker Enqueues PDF Job

CODE
import { InternalJobClient } from "@servicebridge/node";

const internalJobs = new InternalJobClient({
  endpoint: "https://api.servicebridge.com",
  serviceKey: process.env.BOOKSTORE_SERVICE_API_KEY
});

worker.on("POST /order", async (job, ctx) => {
  const order = await bookstore.createOrder(job.payload);

  const pdfJob = await internalJobs.enqueue({
    targetServiceId: "svc_pdf_generator",
    operation: "generate_invoice_pdf",
    payload: {
      order_id: order.id,
      customer_id: order.customerId,
      template: "invoice_v1"
    },
    idempotencyKey: `invoice:${order.id}`
  });

  return {
    statusCode: 201,
    body: {
      order_id: order.id,
      invoice_job_id: pdfJob.jobId,
      invoice_status: "generating"
    }
  };
});

Equivalent Raw API

POST
POST /v1/internal/jobs
Authorization: Bearer sk_live_bookstore_service
Idempotency-Key: invoice:order_789

{
  "target_service_id": "svc_pdf_generator",
  "operation": "generate_invoice_pdf",
  "payload": {
    "order_id": "order_789",
    "customer_id": "cust_456",
    "template": "invoice_v1"
  }
}

Wait for Internal Job Result

CODE
const pdfJob = await internalJobs.enqueue({
  targetServiceId: "svc_pdf_generator",
  operation: "generate_invoice_pdf",
  payload: { order_id: order.id }
});

const result = await internalJobs.waitForResult(pdfJob.jobId, {
  timeoutMs: 120000,
  pollIntervalMs: 2000
});

await bookstore.attachInvoice(order.id, result.body.pdf_url);
Waiting is useful when the caller needs the result immediately, but it ties up worker capacity. For most workflows, return immediately and use a follow-up job or event rule.

Internal Permission Required

CONFIG
{
  "source_service_id": "svc_bookstore",
  "target_service_id": "svc_pdf_generator",
  "operation": "generate_invoice_pdf",
  "required_scopes": ["pdf:generate"],
  "rate_limit_policy": "internal_pdf_generation",
  "max_pending_jobs": 5000
}

SDK Demo Strategy

Five progressive implementations

These SDK demos mirror the API use cases. Each level adds one capability so developers can adopt ServiceBridge gradually: first a simple worker, then authenticated users, then internal jobs, then multi-service orchestration, then production-grade worker operations.

Level 1 SDK: Simple Public Catalog Worker

CODE

A private bookstore worker handles public catalog reads. The developer only writes the business function; the SDK handles polling, claiming, completion, failure, and heartbeats.

import { ServiceBridgeWorker } from "@servicebridge/node";
import { books } from "./bookstore-db";

const worker = new ServiceBridgeWorker({
  endpoint: process.env.SERVICEBRIDGE_ENDPOINT,
  serviceId: "svc_bookstore",
  workerKey: process.env.BOOKSTORE_WORKER_KEY,
  workerId: "bookstore-catalog-worker-1",
  concurrency: 10
});

worker.on("GET /book", async (job, ctx) => {
  const result = await books.search({
    search: job.query.search,
    limit: Number(job.query.limit || 20)
  });

  return {
    statusCode: 200,
    body: { books: result }
  };
});

await worker.start();

Level 2 SDK: Authenticated Cart and Order Actions

CODE

ServiceBridge validates the JWT at ingress. The worker receives normalized auth_context and can enforce application-level business rules using ctx.requireScope().

worker.on("POST /cart", async (job, ctx) => {
  ctx.requireScope("cart:create");

  const cart = await carts.create({
    userId: ctx.auth.user.sub,
    items: job.payload.items
  });

  return {
    statusCode: 201,
    body: cart
  };
});

worker.on("POST /order", async (job, ctx) => {
  ctx.requireScope("order:create");

  const order = await orders.createDraft({
    userId: ctx.auth.user.sub,
    cartId: job.payload.cart_id
  });

  return {
    statusCode: 202,
    body: {
      order_id: order.id,
      status: "processing"
    }
  };
});
Auth context exposed by SDK
{
  "callerType": "frontend_user",
  "user": {
    "sub": "user_456",
    "scopes": ["cart:create", "order:create"],
    "roles": ["customer"],
    "email": "customer@example.com"
  },
  "matchedAccessRuleId": "rule_customer_create_order"
}

Level 3 SDK: Enqueue Internal PDF Job

CODE

The Bookstore worker creates an order and enqueues a PDF job. The PDF worker is on another machine and does not need a public endpoint.

import {
  ServiceBridgeWorker,
  InternalJobClient
} from "@servicebridge/node";

const worker = new ServiceBridgeWorker({
  endpoint: process.env.SERVICEBRIDGE_ENDPOINT,
  serviceId: "svc_bookstore",
  workerKey: process.env.BOOKSTORE_WORKER_KEY
});

const internalJobs = new InternalJobClient({
  endpoint: process.env.SERVICEBRIDGE_ENDPOINT,
  serviceKey: process.env.BOOKSTORE_SERVICE_API_KEY
});

worker.on("POST /order", async (job, ctx) => {
  ctx.requireScope("order:create");

  const order = await orders.confirm({
    userId: ctx.auth.user.sub,
    cartId: job.payload.cart_id
  });

  const invoice = await internalJobs.enqueue({
    targetServiceId: "svc_pdf_generator",
    operation: "generate_invoice_pdf",
    payload: {
      order_id: order.id,
      customer_id: order.customerId,
      template: "invoice_v1"
    },
    idempotencyKey: `invoice:${order.id}`,
    parentJobId: job.id,
    traceId: ctx.traceId
  });

  return {
    statusCode: 201,
    body: {
      order_id: order.id,
      status: "confirmed",
      invoice_status: "generating",
      invoice_job_id: invoice.jobId
    }
  };
});

await worker.start();
PDF worker
const pdfWorker = new ServiceBridgeWorker({
  endpoint: process.env.SERVICEBRIDGE_ENDPOINT,
  serviceId: "svc_pdf_generator",
  workerKey: process.env.PDF_WORKER_KEY,
  workerId: "pdf-worker-1",
  concurrency: 4
});

pdfWorker.operation("generate_invoice_pdf", async (job, ctx) => {
  const pdf = await renderInvoicePdf(job.payload);
  const uploaded = await storage.upload(pdf);

  return {
    body: {
      pdf_url: uploaded.url,
      pdf_id: uploaded.id
    }
  };
});

await pdfWorker.start();

Level 4 SDK: Multi-Service Order Orchestration

CODE

A realistic order workflow fans out to inventory, payment, PDF, and notification services. Each internal call is checked by Internal Job Permissions.

worker.on("POST /order", async (job, ctx) => {
  ctx.requireScope("order:create");

  const order = await orders.createPending({
    userId: ctx.auth.user.sub,
    items: job.payload.items
  });

  const inventoryJob = await internalJobs.enqueue({
    targetServiceId: "svc_inventory",
    operation: "reserve_stock",
    payload: {
      order_id: order.id,
      items: job.payload.items
    },
    idempotencyKey: `reserve:${order.id}`,
    parentJobId: job.id,
    traceId: ctx.traceId
  });

  const paymentJob = await internalJobs.enqueue({
    targetServiceId: "svc_payment",
    operation: "authorize_payment",
    payload: {
      order_id: order.id,
      amount: order.total,
      currency: "USD",
      payment_method_id: job.payload.payment_method_id
    },
    idempotencyKey: `payment:${order.id}`,
    parentJobId: job.id,
    traceId: ctx.traceId
  });

  return {
    statusCode: 202,
    body: {
      order_id: order.id,
      status: "processing",
      jobs: {
        inventory: inventoryJob.jobId,
        payment: paymentJob.jobId
      }
    }
  };
});
Follow-up event-driven operation
// When payment and inventory complete,
// ServiceBridge workflow or Bookstore worker can enqueue:
await internalJobs.enqueue({
  targetServiceId: "svc_notification",
  operation: "send_order_confirmation",
  payload: {
    order_id: order.id,
    email: ctx.auth.user.email
  },
  idempotencyKey: `confirmation:${order.id}`
});

Level 5 SDK: Production Worker Runtime

CODE

The production worker config adds concurrency, leases, retries, graceful shutdown, idempotency, structured logs, tracing, and schema validation.

import { z } from "zod";
import {
  ServiceBridgeWorker,
  RetryableError,
  PermanentError
} from "@servicebridge/node";

const CreateOrder = z.object({
  items: z.array(z.object({
    sku: z.string(),
    quantity: z.number().int().positive()
  })),
  payment_method_id: z.string()
});

const worker = new ServiceBridgeWorker({
  endpoint: process.env.SERVICEBRIDGE_ENDPOINT,
  serviceId: "svc_bookstore",
  workerKey: process.env.BOOKSTORE_WORKER_KEY,

  workerId: process.env.HOSTNAME,
  concurrency: 20,
  maxBatchSize: 10,
  leaseSeconds: 90,
  autoExtendLease: true,
  heartbeatIntervalMs: 15000,
  handlerTimeoutMs: 60000,
  gracefulShutdownTimeoutMs: 30000,

  retryPolicy: {
    initialDelayMs: 1000,
    maxDelayMs: 60000,
    multiplier: 2,
    jitter: true
  }
});

worker.use(async (job, ctx, next) => {
  ctx.logger.info({
    trace_id: ctx.traceId,
    job_id: job.id,
    operation: job.operation || `${job.method} ${job.path}`
  }, "job_started");

  return next();
});

worker.on("POST /order", async (job, ctx) => {
  const input = CreateOrder.parse(job.payload);

  try {
    return await createOrderWorkflow(input, ctx);
  } catch (err) {
    if (err.code === "PAYMENT_TIMEOUT") {
      throw new RetryableError("Payment provider timeout", {
        retryAfterSeconds: 30
      });
    }

    throw new PermanentError("Order creation failed", {
      statusCode: 400,
      details: err.message
    });
  }
});

await worker.start();

AI Chat SDK Example: Agent Worker with Tool Fan-Out

CODE
const agentWorker = new ServiceBridgeWorker({
  endpoint: process.env.SERVICEBRIDGE_ENDPOINT,
  serviceId: "svc_agent_orchestrator",
  workerKey: process.env.AGENT_WORKER_KEY,
  concurrency: 8
});

const internalJobs = new InternalJobClient({
  endpoint: process.env.SERVICEBRIDGE_ENDPOINT,
  serviceKey: process.env.AGENT_SERVICE_API_KEY
});

agentWorker.operation("run_agent_turn", async (job, ctx) => {
  const ragJob = await internalJobs.enqueue({
    targetServiceId: "svc_rag",
    operation: "retrieve_context",
    payload: {
      conversation_id: job.payload.conversation_id,
      query: job.payload.prompt
    },
    parentJobId: job.id,
    traceId: ctx.traceId
  });

  const webJob = await internalJobs.enqueue({
    targetServiceId: "svc_web_search",
    operation: "web_search",
    payload: {
      query: job.payload.prompt,
      max_results: 5
    },
    parentJobId: job.id,
    traceId: ctx.traceId
  });

  const rag = await internalJobs.waitForResult(ragJob.jobId);
  const web = await internalJobs.waitForResult(webJob.jobId);

  const llmJob = await internalJobs.enqueue({
    targetServiceId: "svc_llm",
    operation: "generate_response",
    payload: {
      prompt: job.payload.prompt,
      context: rag.body.documents,
      web_results: web.body.results,
      stream: true,
      result_channel_id: job.result_channel_id
    },
    parentJobId: job.id,
    traceId: ctx.traceId
  });

  return {
    body: {
      status: "answer_generating",
      llm_job_id: llmJob.jobId
    }
  };
});

SDK Reliability Features

Production behavior

The SDK should implement correct job execution behavior by default so developers do not accidentally create duplicate side effects, stuck jobs, or broken shutdown paths.

Feature
SDK Behavior
Why It Matters
Leases
Claims jobs with lease_seconds and auto-extends while running.
Prevents duplicate processing while allowing recovery after crash.
Graceful Shutdown
Stops claiming new jobs and completes or releases active jobs.
Safe deploys and restarts.
Retries
RetryableError marks job retryable with backoff.
Handles transient failures.
DLQ
Permanent failures or max retries go to dead letter queue.
Prevents poison jobs from blocking queue.
Idempotency
Supports idempotency keys for internal enqueue and client calls.
Prevents duplicate order/PDF generation.
Timeouts
Per-handler timeout with fail/retry behavior.
Stops jobs from hanging forever.
Tracing
Propagates trace_id and parent_job_id.
Debug cross-service workflows.

Reliability Config

CONFIG
const worker = new ServiceBridgeWorker({
  serviceId: "svc_bookstore",
  workerKey: process.env.WORKER_KEY,

  concurrency: 10,
  leaseSeconds: 60,
  autoExtendLease: true,

  handlerTimeoutMs: 45000,
  maxHandlerRetries: 3,

  retryPolicy: {
    initialDelayMs: 1000,
    maxDelayMs: 60000,
    multiplier: 2,
    jitter: true
  },

  deadLetterOnPermanentError: true,
  gracefulShutdownTimeoutMs: 30000
});

Idempotent Handler Example

CODE
worker.operation("generate_invoice_pdf", async (job, ctx) => {
  const existing = await db.invoices.findByIdempotencyKey(ctx.idempotencyKey);

  if (existing) {
    return { body: existing };
  }

  const pdf = await generatePdf(job.payload);

  await db.invoices.insert({
    idempotencyKey: ctx.idempotencyKey,
    orderId: job.payload.order_id,
    pdfUrl: pdf.url
  });

  return { body: { pdf_url: pdf.url } };
});

SDK Security Model

Safe defaults

The SDK should make the secure path easy. It should never ask developers to manually trust caller-supplied service IDs, raw JWTs, or unverified source identities.

Worker Key Scope

KEY
{
  "key_type": "worker",
  "tenant_id": "tenant_123",
  "service_id": "svc_pdf_generator",
  "permissions": [
    "jobs:claim",
    "jobs:complete",
    "jobs:fail",
    "heartbeat:write"
  ]
}
Worker keys should not automatically enqueue jobs to other services. Use service API keys for InternalJobClient.

Accessing auth_context

CODE
worker.on("POST /book", async (job, ctx) => {
  // This was validated by ServiceBridge at ingress
  const userId = ctx.auth.user.sub;
  const scopes = ctx.auth.user.scopes;

  ctx.requireScope("book:create");

  return createBook({
    ...job.payload,
    createdBy: userId
  });
});
The SDK should expose normalized auth_context, not force every worker to parse and validate raw JWTs. Backend revalidation can be optional defense-in-depth.
Security Concern
SDK Default
Reason
JWT trust
Use ServiceBridge auth_context.
JWT was validated at ingress before job creation.
Internal caller identity
Derived from service key by ServiceBridge.
Prevents source_service_id spoofing.
Secrets
Read keys from env/secrets manager.
Do not hardcode keys.
Payload validation
Support schema validators.
Business payload still needs validation.
Replay
Support idempotency keys and signed service requests.
Prevents duplicate side effects.

SDK API Reference

Proposed TypeScript interface

This is the proposed public API for the Node SDK. Other languages should keep equivalent concepts.

ServiceBridgeWorker

TYPE
class ServiceBridgeWorker {
  constructor(config: WorkerConfig)

  on(route: string, handler: RouteJobHandler): void
  operation(name: string, handler: OperationJobHandler): void

  use(middleware: WorkerMiddleware): void

  start(): Promise
  stop(options?: StopOptions): Promise

  heartbeat(): Promise
  claim(): Promise
  complete(jobId: string, result: JobResult): Promise
  fail(jobId: string, error: JobError): Promise
}

InternalJobClient

TYPE
class InternalJobClient {
  constructor(config: {
    endpoint: string
    serviceKey: string
  })

  enqueue(input: {
    targetServiceId: string
    operation: string
    payload: unknown
    idempotencyKey?: string
    priority?: "low" | "normal" | "high"
    delayUntil?: string
    parentJobId?: string
    traceId?: string
  }): Promise<{ jobId: string; status: "pending" }>

  getResult(jobId: string): Promise

  waitForResult(
    jobId: string,
    options?: {
      timeoutMs?: number
      pollIntervalMs?: number
    }
  ): Promise
}

ServiceBridgeClient

TYPE
class ServiceBridgeClient {
  constructor(config: {
    endpoint: string
    serviceSlug: string
    publicClientKey?: string
    serviceKey?: string
    getUserToken?: () => Promise | string
  })

  get(path: string, options?: RequestOptions): Promise
  post(path: string, body?: unknown, options?: RequestOptions): Promise
  put(path: string, body?: unknown, options?: RequestOptions): Promise
  patch(path: string, body?: unknown, options?: RequestOptions): Promise
  delete(path: string, options?: RequestOptions): Promise

  getResult(jobId: string): Promise
  waitForResult(jobId: string, options?: WaitOptions): Promise
  cancel(jobId: string): Promise
}

Types

TYPE
type Job = {
  id: string
  tenantId: string
  targetServiceId: string
  sourceServiceId?: string
  method?: string
  path?: string
  operation?: string
  payload: unknown
  query: Record
  headers: Record
  authContext: AuthContext
  traceId: string
  idempotencyKey?: string
}

type AuthContext = {
  callerType: "public_client" | "frontend_user" | "service_api" | "worker"
  publicClientKeyId?: string
  serviceKeyId?: string
  jwtProviderId?: string
  user?: {
    sub: string
    issuer: string
    audience: string
    scopes: string[]
    roles?: string[]
    email?: string
  }
  matchedAccessRuleId?: string
}

type JobResult = {
  statusCode?: number
  headers?: Record
  body?: unknown
}

ServiceBridge CLI Overview

sbctl

The ServiceBridge CLI, proposed as sbctl, lets developers and machines provision ServiceBridge deterministically. It is the control-plane automation product for creating services, resources, access rules, IAM providers, internal job permissions, rate limits, worker groups, alerts, network policies, and tenant automation keys.

Recommended product model: YAML desired state + plan/apply workflow + tenant automation key authentication.

Declarative

YAML desired state

Define the full ServiceBridge tenant configuration in servicebridge.yaml.

Deterministic

Plan/apply

Preview changes, detect drift, and apply the exact intended config.

Automatable

CI/CD friendly

Use tenant automation keys in GitHub Actions, GitLab CI, Jenkins, or shell scripts.

CLI Quickstart

CLI
npm install -g @servicebridge/cli

export SERVICEBRIDGE_TENANT_KEY=tk_live_xxx

sbctl whoami
sbctl init bookstore
sbctl validate -f servicebridge.yaml
sbctl plan -f servicebridge.yaml
sbctl apply -f servicebridge.yaml

What the CLI Manages

SCOPE
services
resources
method access rules
rate limit policies
public client keys
service API keys
worker keys
tenant automation keys
IAM / JWT providers
internal job permissions
network and DDoS policy
worker groups
alerts
billing usage reads
environment promotion

Tenant Automation Keys

Machine access for the control plane

Tenant automation keys authenticate the CLI and CI/CD systems. These keys are different from public client keys, service API keys, and worker keys. They are allowed to manage tenant configuration, not invoke business routes or claim jobs.

Use tenant automation keys for provisioning. Do not use service API keys for control-plane infrastructure management.

Create Tenant Automation Key

POST
POST /v1/tenant/automation-keys
Authorization: Bearer tenant_admin_token

{
  "name": "github-actions-prod",
  "environment": "prod",
  "scopes": [
    "tenant:read",
    "services:write",
    "resources:write",
    "access_rules:write",
    "rate_limits:write",
    "iam_providers:write",
    "internal_permissions:write",
    "network:write",
    "alerts:write"
  ],
  "allowed_ips": ["203.0.113.20/32"],
  "expires_at": "2027-01-01T00:00:00Z"
}
{
  "key_id": "tak_123",
  "prefix": "tk_live_github_prod",
  "secret": "tk_live_github_prod_7wUj...",
  "warning": "This secret is shown only once. Store it in your CI secret manager."
}
Key Type
Prefix
Purpose
Public Client Key
pk_live_
Frontend/app identity for data-plane calls. Not secret.
Service API Key
sk_live_
Backend service identity for M2M and internal job enqueue.
Worker Key
wk_live_
Worker identity for claim/complete/fail jobs.
Tenant Automation Key
tk_live_
Machine identity for control-plane provisioning through CLI/API.

Tenant Automation Key Enforcement

FLOW
1. Validate tk_live key hash
2. Derive tenant_id from key
3. Reject if expired or revoked
4. Check optional IP allowlist
5. Check requested control-plane scope
6. Apply tenant automation rate limit
7. Audit every create/update/delete
8. Execute config mutation or dry-run plan

Install & Authenticate

Developer and machine setup

The CLI should support local developer login, environment variables, and non-interactive CI/CD authentication.

Install

NPM
npm install -g @servicebridge/cli

# or
brew install servicebridge/tap/sbctl

# or
curl -fsSL https://install.servicebridge.com | sh

Local Developer Auth

CLI
sbctl login

# Opens browser OAuth login
# Stores a short-lived developer session locally
# Useful for humans, not CI/CD

Machine / CI Auth

CLI
export SERVICEBRIDGE_TENANT_KEY=tk_live_xxx
sbctl whoami
sbctl config set endpoint https://api.servicebridge.com
sbctl config set tenant-key tk_live_xxx
sbctl whoami
Auth Method
Best For
Notes
sbctl login
Human developers
OAuth/browser flow, short-lived session.
SERVICEBRIDGE_TENANT_KEY
CI/CD and bash scripts
Non-interactive deterministic provisioning.
--tenant-key
One-off automation
Useful but avoid exposing in shell history.
Secret manager integration
Production pipelines
GitHub Secrets, Vault, AWS Secrets Manager, etc.

YAML Configuration

servicebridge.yaml

The CLI should support a declarative YAML file as the primary IaC format. YAML is readable, reviewable in pull requests, and easy to generate from scripts.

Recommended File Structure

YAML
apiVersion: servicebridge.io/v1
kind: TenantConfig
metadata:
  name: bookstore-prod
  environment: prod

spec:
  iamProviders: []
  rateLimitPolicies: []
  services: []
  internalJobPermissions: []
  networkPolicy: {}
  alerts: []

Full Bookstore YAML

YAML
apiVersion: servicebridge.io/v1
kind: TenantConfig
metadata:
  name: bookstore-prod
  environment: prod

spec:
  iamProviders:
    - name: bookstore-auth
      type: oidc
      issuer: https://auth.bookstore.com/
      discoveryUrl: https://auth.bookstore.com/.well-known/openid-configuration
      jwksUri: https://auth.bookstore.com/.well-known/jwks.json
      audiences:
        - bookstore-api
      allowedAlgorithms:
        - RS256
      claimMapping:
        subject: sub
        scope: scope
        roles: https://bookstore.com/roles
        tenant: https://bookstore.com/tenant_id
        email: email

  rateLimitPolicies:
    - name: public_get_strict
      limit: 120
      window: 1m
      burst: 20
      dimensions:
        - public_client_key_id
        - ip_address
        - origin
        - resource_id
        - method

    - name: frontend_write_strict
      limit: 30
      window: 1m
      burst: 5
      dimensions:
        - public_client_key_id
        - user_id
        - ip_address
        - resource_id
        - method

    - name: internal_pdf_generation
      limit: 600
      window: 1m
      burst: 100
      dimensions:
        - tenant_id
        - source_service_id
        - target_service_id
        - operation

  services:
    - name: Book Store
      slug: bookstore
      slugRouting: "Slug Enabled"
      resources:
        - path: /book
          version: v1
          methods:
            - GET
            - POST
            - PUT
            - PATCH
            - DELETE
          accessRules:
            - name: Public Book Read
              method: GET
              callerType: public_client
              authMode: public_client_token
              rateLimitPolicy: public_get_strict
              allowedOrigins:
                - https://bookstore.com

            - name: Frontend Book Create
              method: POST
              callerType: frontend_user
              authMode: public_client_plus_user_token
              jwtProvider: bookstore-auth
              requiredScopes:
                - book:create
              rateLimitPolicy: frontend_write_strict
              allowedOrigins:
                - https://bookstore.com

    - name: PDF Generator
      slug: pdf-generator
      slugRouting: "No slug: internal jobs"
      operations:
        - generate_invoice_pdf
        - generate_receipt_pdf

  internalJobPermissions:
    - sourceService: Book Store
      targetService: PDF Generator
      operation: generate_invoice_pdf
      requiredScopes:
        - pdf:generate
      rateLimitPolicy: internal_pdf_generation
      maxPendingJobs: 5000

  networkPolicy:
    wafMode: challenge
    botProtection: true
    tenantGlobalRpm: 10000

  alerts:
    - severity: critical
      signal: DLQ jobs
      condition: DLQ > 5 for 5m
      channel: Slack #ops
Secrets should not be stored in YAML. YAML should reference key names, scopes, and policies. Secret values should be created once and stored in a secrets manager.

CLI Commands

Command surface

The CLI should support both declarative IaC commands and direct imperative commands for quick scripting.

Command
Purpose
Example
sbctl init
Create starter YAML.
sbctl init bookstore
sbctl validate
Validate schema and references.
sbctl validate -f servicebridge.yaml
sbctl plan
Preview changes.
sbctl plan -f servicebridge.yaml
sbctl apply
Apply desired state.
sbctl apply -f servicebridge.yaml
sbctl diff
Compare local config to remote.
sbctl diff -f servicebridge.yaml
sbctl export
Export existing tenant config.
sbctl export --env prod
sbctl destroy
Remove config with confirmation.
sbctl destroy -f old.yaml --confirm
sbctl keys create
Create automation/service/worker keys.
sbctl keys create tenant-automation
sbctl jobs tail
Watch jobs.
sbctl jobs tail --service bookstore
sbctl workers list
List worker health.
sbctl workers list

Plan Output Example

CLI
sbctl plan -f servicebridge.yaml

Plan:
  + create iamProvider bookstore-auth
  + create rateLimit public_get_strict
  + create service Book Store
  + create resource Book Store /book
  + create accessRule Public Book Read
  + create service PDF Generator
  + create internalJobPermission Book Store → PDF Generator generate_invoice_pdf
  ~ update networkPolicy wafMode: monitor → challenge

No destructive changes.

Run sbctl apply -f servicebridge.yaml to apply.

Imperative Scripting Example

BASH
#!/usr/bin/env bash
set -euo pipefail

export SERVICEBRIDGE_TENANT_KEY="$SB_TENANT_KEY"

sbctl services create \
  --name "Book Store" \
  --slug bookstore \
  --slug-enabled true

sbctl resources create \
  --service bookstore \
  --path /book \
  --methods GET,POST,PUT,PATCH,DELETE

sbctl apply -f servicebridge.yaml

CLI Demo Strategy

Five provisioning examples

These CLI demos show how teams move from a single public endpoint to a production multi-service e-commerce configuration managed by YAML, tenant automation keys, plan/apply, and CI/CD.

Level 1 CLI: Provision Public Catalog

YAML
apiVersion: servicebridge.io/v1
kind: TenantConfig
metadata:
  name: bookstore-level-1
  environment: dev

spec:
  rateLimitPolicies:
    - name: public_get_strict
      limit: 120
      window: 1m
      burst: 20
      dimensions:
        - public_client_key_id
        - ip_address
        - origin
        - resource_id
        - method

  services:
    - name: Book Store
      slug: bookstore
      slugRouting: "Slug Enabled"
      resources:
        - path: /book
          version: v1
          methods: [GET]
          accessRules:
            - name: Public Catalog Read
              method: GET
              callerType: public_client
              authMode: public_client_token
              rateLimitPolicy: public_get_strict
              allowedOrigins:
                - https://bookstore.com
sbctl validate -f servicebridge.level1.yaml
sbctl plan -f servicebridge.level1.yaml
sbctl apply -f servicebridge.level1.yaml

Level 2 CLI: Add IAM and Authenticated Customer Actions

YAML
spec:
  iamProviders:
    - name: bookstore-auth
      type: oidc
      issuer: https://auth.bookstore.com/
      jwksUri: https://auth.bookstore.com/.well-known/jwks.json
      audiences: [bookstore-api]
      allowedAlgorithms: [RS256]
      claimMapping:
        subject: sub
        scope: scope
        roles: https://bookstore.com/roles
        email: email

  services:
    - name: Book Store
      slug: bookstore
      slugRouting: "Slug Enabled"
      resources:
        - path: /cart
          methods: [POST]
          accessRules:
            - name: Customer Create Cart
              method: POST
              callerType: frontend_user
              authMode: public_client_plus_user_token
              jwtProvider: bookstore-auth
              requiredScopes: [cart:create]
              rateLimitPolicy: frontend_write_strict

        - path: /order
          methods: [POST]
          accessRules:
            - name: Customer Create Order
              method: POST
              callerType: frontend_user
              authMode: public_client_plus_user_token
              jwtProvider: bookstore-auth
              requiredScopes: [order:create]
              rateLimitPolicy: frontend_write_strict

Level 3 CLI: Add Internal PDF Generator

YAML
spec:
  services:
    - name: Book Store
      slug: bookstore
      slugRouting: "Slug Enabled"

    - name: PDF Generator
      slug: pdf-generator
      slugRouting: "No slug: internal jobs"
      operations:
        - generate_invoice_pdf
        - generate_receipt_pdf

  rateLimitPolicies:
    - name: internal_pdf_generation
      limit: 600
      window: 1m
      burst: 100
      dimensions:
        - tenant_id
        - source_service_id
        - target_service_id
        - operation

  internalJobPermissions:
    - sourceService: Book Store
      targetService: PDF Generator
      operation: generate_invoice_pdf
      requiredScopes: [pdf:generate]
      rateLimitPolicy: internal_pdf_generation
      maxPendingJobs: 5000
After this apply, Book Store can enqueue PDF jobs, but no other service can unless explicitly granted.

Level 4 CLI: Multi-Service E-Commerce Workflow

YAML
spec:
  services:
    - name: Book Store
      slug: bookstore
      slugRouting: "Slug Enabled"

    - name: Inventory Service
      slug: inventory
      slugRouting: "No slug: internal jobs"
      operations: [reserve_stock, release_stock]

    - name: Payment Service
      slug: payment
      slugRouting: "No slug: internal jobs"
      operations: [authorize_payment, capture_payment, refund_payment]

    - name: PDF Generator
      slug: pdf-generator
      slugRouting: "No slug: internal jobs"
      operations: [generate_invoice_pdf]

    - name: Notification Service
      slug: notification
      slugRouting: "No slug: internal jobs"
      operations: [send_order_confirmation, send_refund_notice]

  internalJobPermissions:
    - sourceService: Book Store
      targetService: Inventory Service
      operation: reserve_stock
      requiredScopes: [inventory:reserve]
      rateLimitPolicy: internal_inventory

    - sourceService: Book Store
      targetService: Payment Service
      operation: authorize_payment
      requiredScopes: [payment:authorize]
      rateLimitPolicy: internal_payment

    - sourceService: Book Store
      targetService: PDF Generator
      operation: generate_invoice_pdf
      requiredScopes: [pdf:generate]
      rateLimitPolicy: internal_pdf_generation

    - sourceService: Book Store
      targetService: Notification Service
      operation: send_order_confirmation
      requiredScopes: [notification:send]
      rateLimitPolicy: internal_notification

Level 5 CLI: Production GitOps Rollout

YAML
# .github/workflows/servicebridge-prod.yml
name: ServiceBridge Production Apply

on:
  push:
    branches: [main]
    paths:
      - servicebridge.yaml

jobs:
  servicebridge:
    runs-on: ubuntu-latest
    environment: production

    steps:
      - uses: actions/checkout@v4
      - run: npm install -g @servicebridge/cli

      - name: Validate
        run: sbctl validate -f servicebridge.yaml --env prod
        env:
          SERVICEBRIDGE_TENANT_KEY: ${{ secrets.SERVICEBRIDGE_TENANT_KEY }}

      - name: Plan
        run: sbctl plan -f servicebridge.yaml --env prod --format json > plan.json
        env:
          SERVICEBRIDGE_TENANT_KEY: ${{ secrets.SERVICEBRIDGE_TENANT_KEY }}

      - name: Apply
        run: sbctl apply -f servicebridge.yaml --env prod --non-interactive
        env:
          SERVICEBRIDGE_TENANT_KEY: ${{ secrets.SERVICEBRIDGE_TENANT_KEY }}
Production tenant automation key
{
  "name": "github-actions-prod",
  "environment": "prod",
  "scopes": [
    "tenant:read",
    "services:write",
    "resources:write",
    "access_rules:write",
    "rate_limits:write",
    "iam_providers:write",
    "internal_permissions:write",
    "network:write",
    "alerts:write"
  ],
  "allowed_ips": ["203.0.113.20/32"],
  "expires_at": "2027-01-01T00:00:00Z"
}

CI/CD & GitOps

Apply from pipelines

The CLI is designed for pull-request reviewed infrastructure changes. Developers change YAML, CI validates and plans, then production apply happens after approval.

GitHub Actions Example

YAML
name: ServiceBridge Apply

on:
  push:
    branches: [main]
    paths:
      - servicebridge.yaml

jobs:
  apply:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install ServiceBridge CLI
        run: npm install -g @servicebridge/cli

      - name: Validate
        run: sbctl validate -f servicebridge.yaml
        env:
          SERVICEBRIDGE_TENANT_KEY: ${{ secrets.SERVICEBRIDGE_TENANT_KEY }}

      - name: Plan
        run: sbctl plan -f servicebridge.yaml --env prod
        env:
          SERVICEBRIDGE_TENANT_KEY: ${{ secrets.SERVICEBRIDGE_TENANT_KEY }}

      - name: Apply
        run: sbctl apply -f servicebridge.yaml --env prod --non-interactive
        env:
          SERVICEBRIDGE_TENANT_KEY: ${{ secrets.SERVICEBRIDGE_TENANT_KEY }}

Recommended PR Workflow

FLOW
pull request:
  sbctl validate
  sbctl plan
  comment plan output on PR

merge to main:
  sbctl apply --non-interactive

production:
  require approval for destructive changes
  require tenant automation key with IP allowlist
  audit every apply

CLI Security

Provisioning safely

A tenant automation key can change production configuration. Treat it as a powerful control-plane credential.

Risk
Control
Recommendation
Leaked tenant key
Scope restrictions, expiry, rotation, audit logs.
Use CI secrets manager and short expiry.
Bad config apply
validate, plan, approval, non-destructive default.
Require plan review before apply.
Destructive changes
--confirm-destroy or approval gate.
Never delete resources silently.
Wrong environment
environment field and --env validation.
Separate keys for dev/staging/prod.
Unexpected drift
sbctl diff and drift detection.
Fail CI if remote differs unexpectedly.

Least-Privilege Key for CI

JSON
{
  "name": "github-actions-prod",
  "environment": "prod",
  "scopes": [
    "tenant:read",
    "services:write",
    "resources:write",
    "access_rules:write",
    "rate_limits:write",
    "iam_providers:write",
    "internal_permissions:write"
  ],
  "denied_scopes": [
    "keys:write",
    "billing:write"
  ],
  "allowed_ips": ["203.0.113.20/32"],
  "expires_at": "2027-01-01T00:00:00Z"
}

CLI Reference

Proposed command reference

The CLI should offer a compact command surface for both declarative and imperative workflows.

Global Options

CLI
sbctl [command] [options]

Global options:
  --endpoint           ServiceBridge API endpoint
  --tenant-key         Tenant automation key
  --env               Environment name
  --format json|table|yaml  Output format
  --non-interactive         Disable prompts
  --yes                    Auto-confirm safe prompts
  --debug                  Verbose debug logs

Declarative Commands

CLI
sbctl init [name]
sbctl validate -f servicebridge.yaml
sbctl plan -f servicebridge.yaml
sbctl apply -f servicebridge.yaml
sbctl diff -f servicebridge.yaml
sbctl export --env prod
sbctl destroy -f servicebridge.yaml --confirm

Imperative Commands

CLI
sbctl services list
sbctl services create --name "Book Store" --slug bookstore
sbctl resources create --service bookstore --path /book --methods GET,POST
sbctl rules create --service bookstore --path /book --method GET --caller public_client
sbctl iam create --issuer https://auth.bookstore.com/
sbctl internal-permissions create --source bookstore --target pdf-generator --operation generate_invoice_pdf
sbctl keys create tenant-automation --name github-actions-prod
sbctl jobs tail --service bookstore
sbctl workers list

Services

Slug routing only controls whether the /s/{slug}/... namespace exists. It is not endpoint authorization. Endpoint authorization belongs in access rules.

NameSlugSlug RoutingBase URLStatusActions

Resources

Resources define paths and methods. They do not decide who can call them. That is now handled by Method Access Rules.

ServicePathVersionMethodsStatusActions

Method Access Rules

Multiple rules can exist for the same method. Example: GET /book can have a public-client rule with strict limits and a service-API rule with higher limits.

RuleRouteCaller TypeAuth ModeRate LimitScopesOrigin/IP PolicyPriorityActions

Rule Matrix

This view explains the effective access model per resource and method. Use it to audit whether public, frontend-user, and service-api access are separated properly.

Rate Limits

Rate limits are applied by access rule. Public client rules should include IP and origin dimensions. Service API rules can be looser and should often include IP allowlists.

NameLimitWindowBurstDimensionsBest ForActions

Access Keys

Keys identify caller type. Public client keys are copyable and should never be treated as real user authentication.

TypeNamePrefixTenant / ServiceScopeAllowed Origins/IPsStatusActions

IAM / JWT Providers

Configure how ServiceBridge validates user JWTs from any IAM provider before creating jobs. This lets frontend-user access rules enforce issuer, audience, scopes, roles, and tenant boundaries at ingress.

JWT Validation Pipeline

Ingress authorization
1. Detect frontend_user

Request contains public client key plus user JWT.

2. Resolve IAM provider

Use tenant/service/access-rule configuration to choose issuer and JWKS.

3. Validate JWT

Verify signature, kid, alg, issuer, audience, expiry, nbf, and claims.

4. Enforce access rule

Check required scopes/roles before job creation.

5. Create auth_context

Store normalized user identity and authorization decision on the job.

JWT Test Console

Mock validator

Run mock JWT validation.
NameIssuerAudienceJWKS URIAlgorithmsClaim MappingMapped ServicesStatusActions

Internal Job Permissions

Control which source service can enqueue which operation on which internal target service. Same-tenant is required, but same-tenant alone is not enough — every internal call needs an explicit permission.

Internal Job Enforcement

Source → target authorization
1. Validate service API key
2. Derive tenant_id from key
3. Derive source_service_id from key
4. Read target_service_id and operation from body
5. Check target service belongs to same tenant
6. Check target accepts internal jobs
7. Check Internal Job Permission exists
8. Check required scopes
9. Apply internal rate limit
10. Apply target queue quota
11. Create job

Internal Enqueue Simulator

Mock authorization

Run internal job simulation.
Source ServiceTarget ServiceOperationRequired ScopesRate LimitMax PendingStatusActions

Tenant Automation Keys

Machine-level control-plane keys used by the ServiceBridge CLI, CI/CD pipelines, and infrastructure scripts. These keys can create services, resources, access rules, IAM providers, internal job permissions, rate limits, and other tenant configuration deterministically.

Recommended Key Type

Control plane automation
key_type: tenant_automation
prefix: tk_live_...
used_by:
  - ServiceBridge CLI
  - CI/CD pipelines
  - Terraform-style scripts
  - GitOps deploys

Scope Model

Least privilege
tenant:read
services:write
resources:write
access_rules:write
rate_limits:write
iam_providers:write
internal_permissions:write
keys:write
network:write
alerts:write

Security Controls

Strongly recommended
show secret once
store hash only
optional IP allowlist
expiry date
rotation
audit logs
dry-run support
approval required for destructive apply

CLI Auth Preview

Environment variable
export SERVICEBRIDGE_TENANT_KEY=tk_live_xxx
sbctl whoami
sbctl apply -f servicebridge.yaml

Permission Check Simulator

Mock authorization

Select a key and action.
NamePrefixEnvironmentScopesAllowed IPsExpiresStatusActions

E-Commerce Sanity Test

End-to-end product setup test for a realistic e-commerce client using ServiceBridge. This page verifies that the UI has the right objects, fields, and flows to configure public pages, authenticated customer actions, internal workers, payment services, PDF generation, notifications, CLI provisioning, and production controls.

0/s/{slug} routes
0job-only / m2m
0route auth
0source → target

Target E-Commerce Architecture

Client goal
Public website:
  GET /book
  GET /catalog
  GET /product/:id

Authenticated customer:
  POST /cart
  POST /order
  GET /order/:id

Internal services:
  Inventory Service
  Payment Service
  PDF Generator
  Notification Service
  Fulfillment Service

Automation:
  SDK workers
  CLI YAML
  Tenant automation key
  CI/CD plan → apply

Object Model Verdict

What must exist
Required objects:
  Service
  Resource
  Access Rule
  Rate Limit Policy
  IAM/JWT Provider
  Access Key
  Internal Operation
  Internal Job Permission
  Worker Group
  Tenant Automation Key
  Network Policy
  Alert Rule

Required enforcement:
  JWT validation at ingress
  source service derived from service key
  same-tenant check
  explicit source→target→operation permission
  route-level caller type matching

Step 1 — Public pages and product catalog

Needs
Public visitors can browse catalog/product pages without login, while private database stays behind a worker.
Supported
UI objects
Services → Book Store with slug routing enabled; Resources → GET /book; Method Access Rules → public_client rule; Rate Limits → public_get_strict; Access Keys → public client key with origin allowlist.
Present
Required fields
service slug, slug routing, resource path/methods, caller type, auth mode, allowed origins, public key, public rate-limit dimensions.
Present

Step 2 — Customer login and JWT-authenticated actions

Needs
Customers log in through an IAM provider, receive JWTs, and call cart/order APIs. ServiceBridge validates JWT before creating jobs.
Supported
UI objects
IAM / JWT Providers → issuer/JWKS/audience/claim mapping; Access Rules → frontend_user + public_client_plus_user_token; Resources → POST /cart and POST /order.
Present
Required fields
JWT provider, issuer, JWKS URI, audience, algorithms, subject/scope/role/email claims, required scopes, allowed origins, user-aware rate limits.
Present

Step 3 — Internal payment, inventory, PDF, notification jobs

Needs
Book Store creates internal jobs for Inventory, Payment, PDF Generator, and Notification without direct network access between machines.
Supported
UI objects
Services → Internal Job Only services; Internal Job Permissions → source service, target service, operation, required scopes, rate policy, max pending jobs; Access Keys → service API key for source service.
Patched
Critical fix
Service API keys now have explicit owning service and tenant fields in the model, so internal enqueue source identity can be derived from the key instead of trusted from request body.
Fixed

Step 4 — Worker runtime setup

Needs
Each private service needs workers that poll, claim, heartbeat, execute, complete, fail, and retry jobs.
Supported
UI objects
Workers → service binding, worker heartbeat, concurrency, status; Access Keys → worker key scoped to service; SDK Docs → worker examples.
Mostly present
Future improvement
A dedicated Worker Group object would be cleaner than only individual workers. Current prototype can still represent running workers.
Optional

Step 5 — Production provisioning and operations

Needs
Client wants deterministic setup via YAML, CI/CD, tenant automation key, network controls, alerting, billing, and auditability.
Supported
UI objects
CLI / IaC, Tenant Automation Keys, Network & DDoS, Alerts, Billing, API Docs, SDK Docs, CLI Docs.
Present
Required fields
tenant key scopes, environment, expiry, IP allowlist, YAML spec, plan/apply docs, WAF mode, bot protection, global RPM, alerts.
Present

Commerce Scenario Data Model

Applied by button

Slug-Enabled Service

Book Store
slug routing: enabled
routes:
  GET /book
  GET /catalog
  POST /cart
  POST /order

Internal Services

Inventory Service
Payment Service
PDF Generator
Notification Service
Fulfillment Service

slug routing:
  disabled; internal jobs only

Internal Permissions

Book Store → Inventory.reserve_stock
Book Store → Payment.authorize_payment
Book Store → PDF.generate_invoice_pdf
Book Store → Notification.send_order_confirmation
Book Store → Fulfillment.create_shipment

AI Chatbot Sanity Test

End-to-end setup test for an AI chatbot product using ServiceBridge as the relayer. The frontend uses Google login/JWT, sends prompts as jobs, Agent workers claim jobs, and tool workers perform RAG, web search, MCP/product search, memory, and model inference through internal job permissions.

0slug + internal
0agent → tools
0poll / stream
0agent + tools

Target AI Chat Architecture

Client goal
Frontend:
  Google login
  JWT received
  POST /s/ai-chat/chat
  subscribe to result stream

Slug-Enabled Service:
  AI Chat API

Internal Services:
  Agent Orchestrator
  RAG Retrieval
  Web Search Tool
  MCP Product Search
  Memory Service
  LLM Inference

Runtime:
  prompt job
  tool jobs
  result chunks
  final answer

Sanity Verdict

Patched model
Required additions:
  Result Channels
  conversation_id / message_id in payload
  parent_job_id / trace_id for tool jobs
  internal tool permissions
  token/user rate limits
  AI worker keys
  tool worker keys
  streaming chunk events

Status:
  UI can now configure all critical pieces.

End-to-End Flow

Google JWT → chat job → agent → tools → streamed answer
1. User login
User logs in with Google. Frontend receives an ID token/JWT. ServiceBridge IAM provider validates issuer, audience, signature, expiry, and mapped claims.
${badge('JWT')}
2. Prompt submitted
Frontend calls POST /s/ai-chat/chat with public client key + JWT. ServiceBridge matches a frontend_user access rule.
${badge('Slug enabled')}
3. Chat job created
Job stores prompt, conversation_id, message_id, auth_context, result_channel_id, trace_id, and user rate-limit metadata.
${badge('Job')}
4. Agent claims job
Agent Orchestrator worker claims the chat job, decides which tools are needed, and enqueues internal jobs.
${badge('Worker')}
5. Tool jobs run
RAG, Web Search, MCP Product Search, Memory, and LLM services process jobs if Internal Job Permissions allow the operation.
${badge('Internal')}
6. Result streamed
Agent emits progress events and token chunks to the result channel, then completes the final answer job.
${badge('SSE')}

Step 1 — Google login and JWT validation

Needs
Frontend users must be logged in before sending prompts. ServiceBridge must validate Google/OIDC JWT before creating a chat job.
Supported
UI objects
IAM / JWT Providers → Google Login; Method Access Rules → frontend_user; Access Keys → AI Web Frontend public key.
Present
Required fields
issuer, JWKS URI, audience/client ID, algorithms, subject claim, email claim, required scopes/roles if used.
Present

Step 2 — Chat prompt as slug-routed job

Needs
Prompt is submitted as a job to AI Chat API, with conversation_id and message_id for history and UI state.
Supported
UI objects
Services → AI Chat API; Resources → POST /chat, GET /chat/result; Access Rules → authenticated frontend_user rule; Result Channels → Chat SSE Stream.
Patched
Payload shape
conversation_id, message_id, prompt, model, tools, stream.
Works

Step 3 — Agent worker fans out to tool workers

Needs
Agent worker can enqueue RAG, web search, MCP/product search, memory, and LLM jobs. Not every internal service can call every tool.
Supported
UI objects
Internal Job Permissions configure Agent Orchestrator → RAG/Web/MCP/Memory/LLM operations. Service API key binds source service identity to Agent Orchestrator.
Present
Critical enforcement
ServiceBridge derives source_service_id from the Agent service key. Tool jobs require explicit source→target→operation permission and required scopes.
Works

Step 4 — Result streaming and final response

Needs
AI responses may take time and should stream partial tokens/progress while still storing final result and audit trail.
Supported
UI objects
Result Channels → SSE streaming with chunking enabled, retention policy, auth mode, and route.
Patched
Events
job.accepted, tool.started, tool.completed, result.chunk, job.completed.
Works

Step 5 — Production controls

Needs
Protect expensive LLM/tool workloads using rate limits, per-user quotas, max tool calls, result retention, audit logs, and network controls.
Supported
UI objects
Rate Limits, Alerts, Network & DDoS, Tenant Automation Keys, CLI / IaC, SDK docs, API docs, Queue & Jobs.
Present
Recommended next improvement
Add a dedicated AI Tool Budget object later for max tool calls, token budget, model allowlist, and data-class rules. Current prototype can approximate this with internal permissions + rate limits.
Future

ServiceBridge CLI / IaC

Provision ServiceBridge deterministically from YAML. Developers can define services, resources, access rules, IAM providers, internal job permissions, rate limits, tenant automation keys, workers, alerts, and network policy in source control.

sbctlServiceBridge CLI
YAMLdeterministic config
tk_livetenant automation key
Plan → Applysafe changes

CLI Flow

Infrastructure as configuration
1. Login or export key

Use a tenant automation key locally or inside CI/CD.

2. Define YAML

Write servicebridge.yaml with services, resources, rules, IAM, and internal permissions.

3. Validate

Check schema, missing references, invalid scopes, and destructive operations.

4. Plan

Preview create/update/delete changes before applying.

5. Apply

Idempotently converge ServiceBridge to the desired config.

Quickstart

Bookstore example
npm install -g @servicebridge/cli

export SERVICEBRIDGE_TENANT_KEY=tk_live_xxx

sbctl whoami
sbctl init bookstore
sbctl validate -f servicebridge.yaml
sbctl plan -f servicebridge.yaml
sbctl apply -f servicebridge.yaml

Generated YAML Preview

YAML

Network & DDoS Controls

Application rate limits are not real DDoS protection by themselves. Use edge/WAF controls, bot protection, IP deny lists, and service-key IP allowlists.

Tenant Network Policy

Mock editable

DDoS Readiness

not checked

Workers

Workers poll and execute jobs after the data plane accepts requests.

WorkerServiceHeartbeatConcurrencyStatusActions

Queue & Jobs

Monitor accepted jobs, current worker state, request payloads, progress events, and final responses.

0awaiting workers
0leased
0done
0needs replay
Job IDRouteCallerRuleStatusRetriesActions

Live Job Monitor

select a job
Select a job to inspect its lifecycle.

Request

payload, query, headers
Select a job to inspect its request.

Response

result, error, events
Select a job to inspect its response.

Result Channels

Configure how job results are returned to callers. Most APIs can use polling, but AI chat benefits from streaming partial chunks, stored final results, and traceable result channels linked to a conversation or job.

Polling

Client receives job ID and polls GET /v1/jobs/{job_id}.

SSE Streaming

Client subscribes to a text/event-stream for partial LLM tokens or progress events.

Webhook

ServiceBridge sends completed result to a configured callback endpoint.

Stored Final Result

ServiceBridge stores final response with retention, audit, and trace metadata.

Why this exists

AI chatbot requirement
POST /s/ai-chat/chat
→ returns job_id and result_channel_id

Frontend can:
  poll job result
  or subscribe to SSE stream
  or receive final result later

Agent worker can:
  emit progress events
  emit token chunks
  emit tool-call events
  complete final answer

Result Event Shape

Streaming chunks
{
  "event": "result.chunk",
  "job_id": "job_chat_001",
  "conversation_id": "conv_123",
  "sequence": 42,
  "delta": "The best option is...",
  "trace_id": "trace_ai_abc"
}
NameServiceModeRouteChunkingRetentionAuthStatusActions

Alerts

Alert on queue backlog, DLQ growth, public-client abuse, WAF challenges, and stale workers.

SeveritySignalConditionChannelStatusActions

Security

Security controls shared across the tenant.

Controls

Mocked

Security Findings

not scanned

Billing & Credits

Each tenant has its own prepaid wallet. For credit purchases, please contact your Account Manager.

$0available balance
$0claim available
Managedcontact account manager

Monthly Sandbox Credit

ready to claim

Claim a monthly test credit for local experiments. It expires at the end of the calendar month, then the tenant can claim the next month's grant.

Claim the monthly sandbox credit for this tenant.

Credit Top-Up

account-managed

Create a hosted Checkout Session, redirect the tenant admin to Stripe, and credit the wallet only from the signed webhook event.

Credit Settlement

account-managed
Stripe Checkout stores payment details and sends signed webhook events to settle credits.
Platform Control

SuperAdmin

A separate platform dashboard for users, tenants, credit collection, Stripe checkout configuration, system logs, and service-call telemetry.

First-Run SuperAdmin Onboarding

TOTP required

Create the first platform SuperAdmin, enroll an authenticator app, and then use this account to configure Stripe without editing environment variables.

Authenticator Setup

scan or enter manually
Generate a TOTP setup QR code, scan it with your authenticator app, then enter the 6-digit code.

Verify Setup

6 digits

SuperAdmin Login

TOTP protected
0console accounts
0workspaces
$0current balance $0
Offtenant checkout

Tenant Credit Snapshot

balances and collection
TenantCreditsCollectedLast Credit

Platform Telemetry

system-wide
0
0
0
0
Log in as SuperAdmin to view telemetry.

Tenant Credit Details

all wallets
TenantRegionCreditsCollectedStripe CustomerAction

Manual Credit Adjustment

SuperAdmin only
Choose a tenant and amount to add credits manually.

Tenant Credit Ledger

latest platform-wide entries
TimeTenantAmountSourceNoteCreated By

Stripe Integration

tenant credit checkout

Set test or live Stripe credentials here. Tenants use these settings to open hosted Checkout Sessions for credit purchases. Secret values are never shown again; leave secret fields blank to keep existing values.

Log in as SuperAdmin to manage Stripe.

Service Calls

system-wide job telemetry
TimeCallServiceCallerStatusWorkerEvents

System Logs

audit trail
TimeActorActionTenant

Stripe Webhook Events

latest settlement events
TimeEventStatusDetail

Users

all console accounts
EmailNameStatusCreatedLast Login
Done