Zero Trust Hello World Adapter

This is the public starter repository for developers building adapters against the Zero Trust V2 infrastructure.

It is intentionally small: a Node.js Hello World service plus one demo call to the Zero Trust Control Plane.

Tagline: An open adapter contract and audit envelope for agent action authorization.

Trust Signals

Narrow Scope

ZT-Infra is not trying to be the identity system, policy engine, sandbox, or governance framework for agents.

Instead, it defines the integration contract those layers can meet: request shape, response shape, fail-closed semantics, audit envelope, broker handoff, and conformance tests for agent frameworks.

Use SPIFFE/SPIRE or related systems for workload identity. Use OPA, Cedar, or your governance framework for policy. Use nono, gVisor, Firecracker, Kata, or browser sandboxes for execution containment. Use ZT-Infra to make LangGraph, MCP, A2A, OpenAI wrappers, and custom adapters call those controls consistently before a tool runs and emit the same audit evidence afterward.

This repository is the public on-ramp for contributors who want to help define that adapter contract.

Core Security Docs

Start here if you need to understand the security model before writing code:

Who this is for

Use this repo if you are:

Five-Minute Secure Hello World

This quickstart shows the full security loop:

  1. spin up a local Zero Trust control plane;
  2. register a mock agent;
  3. attempt an unauthorized execution and fail;
  4. apply a policy;
  5. execute a safe Hello World action successfully.

The local control plane is a mock for onboarding. It uses the same /actions request/response shape as the MVP, but its signatures are marked MOCK_ECDSA_SHA_256. For real infrastructure, point ZT_CONTROL_PLANE_URL at a deployed ZT-Infra control plane.

Option A: Docker Compose

Use this path if you want the fewest local prerequisites:

git clone https://github.com/oscarmackjr-twg/zt-adapter-hello-world.git
cd zt-adapter-hello-world
docker compose up

Docker Desktop or another Docker daemon must be running before you start the stack.

In another terminal:

curl -sS http://127.0.0.1:8080/health
curl -sS http://127.0.0.1:8080/demo/deny
curl -sS -X POST http://127.0.0.1:3000/policies/allow \
  -H 'content-type: application/json' \
  -d '{"action":"hello-world.say_hello","reason":"Quickstart policy allows hello world."}'
curl -sS http://127.0.0.1:8080/demo/allow

Expected result:

Stop the stack:

docker compose down

Option B: Local Node.js

1. Install

git clone https://github.com/oscarmackjr-twg/zt-adapter-hello-world.git
cd zt-adapter-hello-world
npm ci
npm test
npm run security:secrets
npm run sbom

2. Start local zt-infra mock

Terminal 1:

npm run zt:mock

Verify:

curl -sS http://127.0.0.1:3000/health | jq .

3. Register a mock agent

Terminal 2:

curl -sS -X POST http://127.0.0.1:3000/agents \
  -H 'content-type: application/json' \
  -d '{"actor":"hello-world-agent"}' | jq .

Expected:

{
  "ok": true,
  "actor": "hello-world-agent",
  "registered": true
}

4. Attempt unauthorized execution

This action is intentionally dangerous and should fail:

npm run demo:deny

Expected:

{
  "ok": false,
  "status": 403,
  "decision": "deny",
  "reason": "Mock policy blocks infrastructure termination.",
  "audit": {
    "kms_signature": {
      "algorithm": "MOCK_ECDSA_SHA_256"
    }
  }
}

5. Apply policy and execute successfully

Allow only the safe Hello World action:

curl -sS -X POST http://127.0.0.1:3000/policies/allow \
  -H 'content-type: application/json' \
  -d '{"action":"hello-world.say_hello","reason":"Quickstart policy allows hello world."}' | jq .

Then execute:

npm run demo:allow

Expected:

{
  "ok": true,
  "status": 200,
  "decision": "allow",
  "executionSkipped": false,
  "result": {
    "message": "Hello from a policy-approved adapter action."
  }
}

6. Run the web adapter

Terminal 3:

cp .env.example .env
npm start

Open:

http://127.0.0.1:8080
http://127.0.0.1:8080/health
http://127.0.0.1:8080/demo/deny
http://127.0.0.1:8080/demo/allow

Requirements

Deploy To Vercel

This repo is Vercel-ready. The browser homepage is served from /, and JSON demo endpoints remain available under the same paths.

  1. Import the GitHub repo into Vercel:
https://github.com/oscarmackjr-twg/zt-adapter-hello-world
  1. Use the default Vercel Node.js settings. No build command is required.
  1. Optional environment variables:
ZT_CONTROL_PLANE_URL=https://your-control-plane.example.com
ZT_ACTOR=hello-world-agent
ZT_TOKEN=optional-bearer-token

Without ZT_CONTROL_PLANE_URL, the homepage and /health still render, while /demo/deny and /demo/allow return a clear 503 explaining that a control plane is not configured.

Vercel routing is defined in vercel.json, with the serverless entry point in api/index.js.

Connect A Real Control Plane

The five-minute flow uses npm run zt:mock.

To use a deployed ZT-Infra-compatible control plane:

  1. deploy or select a control plane that implements the public adapter contract;
  2. confirm it is reachable from the adapter runtime;
  3. set:
ZT_CONTROL_PLANE_URL=http://127.0.0.1:3000
ZT_ACTOR=hello-world-agent

Production control planes should sign audit records with a managed key service and write to a durable audit sink.

Demo Endpoints

The adapter app exposes:

GET /            hello response
GET /health      service health
GET /demo/deny   unauthorized action, expected deny
GET /demo/allow  safe action, expected allow after policy is applied

Denied response shape:

{
  "ok": false,
  "status": 403,
  "decision": "deny",
  "reason": "...",
  "audit": {
    "previous_hash": "...",
    "current_hash": "...",
    "kms_signature": {
      "algorithm": "ECDSA_SHA_256"
    }
  }
}

SDK-Style Usage

This repo includes a tiny public client for the current MVP /actions API.

import { ZeroTrustClient } from "./src/zero-trust-client.js";

const zt = new ZeroTrustClient({
  baseUrl: "http://127.0.0.1:3000",
  actor: "hello-world-agent",
});

const decision = await zt.guardedCall({
  action: "aws.ec2.terminate_instances",
  resource: "i-demo",
  fn: async () => {
    return "this only runs if policy allows";
  },
});

console.log(decision);

Helper methods are included for common adapter surfaces:

await zt.langGraph({ action, nodeName });
await zt.openAIResponses({ action, responseId });
await zt.mcpToolCall({ toolName, resource });
await zt.a2aTask({ externalAgent, resource });

Adapters that need a stable evidence summary can normalize signed audit and DAAL fields:

const evidence = zt.auditEvidence(decision);
console.log(evidence.daalTransactionLink);

See SDK_API.md for the public client API.

Adapter Contract

See ADAPTER_CONTRACT.md.

For identity provisioning, ABAC examples, and audit record semantics, see IDENTITY_AND_POLICY.md.

Execution Brokers

Execution Brokers run approved work after the control plane returns allow.

Examples planned for this public repo:

See CONTRIBUTING.md for the broker contract.

Audit Verification CLI

The repo includes a small verifier for audit-shaped decision responses:

npx zt-audit verify audit.json

or from a local checkout:

node bin/zt-audit.js verify audit.json

The verifier checks required actor/action/decision fields, hash-chain fields, signature metadata, and canonical hash consistency for the public demo record format. Production KMS signature verification is planned work.

Public IaC Example

The public Terraform example deploys an IAM-authorized Lambda Authorization Gateway skeleton:

cd infra/terraform/examples/authorization-gateway
terraform init
terraform apply

The function URL requires AWS IAM/SigV4 authorization. It is not an anonymous public endpoint.

Roadmap

See ROADMAP.md.

Phase 2 extends service identity:

Stable Releases

Stable versions are published as GitHub releases and tags:

v0.1.0

Use releases when linking from public websites or tutorials.

License

This repository is licensed under Apache-2.0. The project uses Apache-2.0 instead of MIT for the public adapter because infrastructure and security adopters usually expect explicit patent grant language.

See LICENSE and NOTICE.

Terminal Demo Recording

The website includes sanitized asciinema recordings of the five-minute flow and Nono broker flow. Only reviewed files under public/ are committed.

public/agent-blocked-then-authorized.cast

Nono Execution Broker recording:

public/nono-sandbox-demo.cast

The website embeds both recordings on /demo.

Raw recordings generated under `recordings/` are ignored by git. Copy a recording into `public/` only after running disclosure checks.

To regenerate them safely, use a clean lab shell with a minimal prompt, no cloud credentials, no real environment variables, and only mock control-plane output. Audit the generated cast before publishing:

asciinema rec --overwrite -c "npm run demo:record" recordings/agent-blocked-then-authorized.cast
asciinema rec --overwrite -c "npm run demo:record:nono" recordings/nono-sandbox-demo.cast
cp recordings/agent-blocked-then-authorized.cast public/agent-blocked-then-authorized.cast
cp recordings/nono-sandbox-demo.cast public/nono-sandbox-demo.cast
npm run security:recordings

Security

Do not commit secrets. Keep .env local.

Report vulnerabilities privately. See SECURITY.md.

For design-level security boundaries, see THREAT_MODEL.md. For launch risks and incident handling, see RISK_REGISTER.md and INCIDENT_RESPONSE.md.

This adapter is a learning repo, not a production agent runtime.