23 services on one Docker network (cd-net). The identity, authorization, and audit path is real: Keycloak, Vault, identity-node, Gitea, Directory, and two Built On Envoy inline OPA boundaries.
flowchart TB
Sarah(("Sarah"))
subgraph OrgA[" Org A "]
KCA["Keycloak A\norg-a realm"]
OC["OpenCode Agent"]
EnvoyA["Built On Envoy\ninline OPA — egress"]
end
subgraph AGNTCY[" AGNTCY shared infrastructure "]
Dir["Directory Node\ngRPC, OASF records"]
IdNode["Identity Node\nCIMD"]
Vault[("Vault\ntransit engine")]
VC["VC Badge"]
end
subgraph OrgB[" Org B "]
KCB["Keycloak B\norg-b realm"]
Envoy["Built On Envoy\ninline OPA"]
Triage["Triage Agent"]
Sub["Sub-Agent\nbounded privilege"]
GW["Gitea Gateway\nscope + deny-list"]
Gitea[("Gitea")]
end
Sarah -->|"OIDC login"| OC
OC -->|"push / search records"| Dir
OC -->|"generate / resolve id"| IdNode
IdNode -.->|"proof JWT signing"| Vault
OC -->|"issue + verify badge"| VC
OC -->|"mint assertion (native, SPI)"| KCA
OC -->|"egress check: assertion"| EnvoyA
EnvoyA -->|"verify JWT + enforce scope, intent, chain"| OC
OC -->|"jwt-bearer exchange"| KCB
OC -->|"POST /api/ticket"| Envoy
Envoy -->|"verify both JWTs + enforce delegation"| Triage
Triage -->|"verify inbound ID-JAG (JWKS)"| KCA
Triage -->|"generate / resolve id (org-b)"| IdNode
Triage -->|"sub-badge scope check"| Envoy
Triage -->|"mint narrowed sub-badge (native, SPI)"| KCB
Triage -->|"push turn / discover delegate"| Dir
Triage -->|"spawn"| Sub
Sub -->|"verify sub-badge (JWKS), then jwt-bearer exchange"| KCB
Sub -->|"generate / resolve id (org-b)"| IdNode
Sub -->|"push turn record"| Dir
Sub -->|"access token + sub-badge"| Envoy
Envoy -->|"enforce operation + signed repository"| GW
GW -->|"admin API"| Gitea
A hop-by-hop sequence, including the policy-gated source read, is on the sequence page. The demo also keeps a styled sequence diagram at cross-domain-id-jag-vc/docs/architecture.md.
| Service | Image | Host port(s) | Purpose |
|---|---|---|---|
keycloak-a |
built from ./keycloak-a (quay.io/keycloak/keycloak:26.7 + keycloak-idjag-spi) |
8082 |
Org A IdP (org-a realm), authenticates Sarah, natively mints ID-JAG assertions via a custom token-exchange SPI |
kc-a-init |
quay.io/keycloak/keycloak:26.7 |
(one-shot) | Registers triage:create optional scope |
keycloak-b |
built from ./keycloak-b (quay.io/keycloak/keycloak:26.7 + keycloak-idjag-spi) |
8083 |
Org B IdP (org-b realm), redeems ID-JAG assertions, natively mints Triage’s narrowed sub-badge |
kc-b-init |
quay.io/keycloak/keycloak:26.7 |
(one-shot) | Registers triage:create/gitea:* optional scopes |
identity-postgres |
postgres:16 |
(internal) | DB for identity-node |
identity-vault |
hashicorp/vault:1.17 |
(internal) | Holds the org-a and org-b trust-authority signing keys (Transit engine) |
identity-node |
ghcr.io/agntcy/identity/node:0.0.23 |
4005 (REST), 4006 (gRPC) |
AGNTCY identity node — CIMD id generate/resolve |
identity-node-init |
python:3.12-slim |
(one-shot) | Bootstraps Vault Transit + registers org-a AND org-b as trust authorities |
dir-postgres |
postgres:16 |
(internal) | Search index DB for the Directory |
dir-zot |
ghcr.io/project-zot/zot:v2.1.17 |
5556 |
OCI registry backing the Directory’s content-addressed storage |
dir-apiserver |
ghcr.io/agntcy/dir-apiserver:v1.6.0 |
8888 |
AGNTCY Directory Node (gRPC only) |
agent-dir-init |
built from ./agent-dir-init |
(one-shot) | Pushes static OASF records for all 3 demo agents |
gitea |
gitea/gitea:1.22 |
3002 (HTTP), 2223 (SSH) |
Protected resource (repo server) |
gitea-init |
gitea/gitea:1.22 |
(one-shot) | Seeds the Gitea admin + demo repo |
gitea-gateway |
built from ./gitea-gateway |
(internal only) | Requires Envoy policy metadata, then rechecks token scope before using Gitea admin credentials |
envoy-org-a |
built from ./envoy-org-a (Envoy + Built On Envoy Composer) |
12000; admin 127.0.0.1:9902 |
Org A gateway; badge-scope PDP (KC-A JWT) + egress JWT + inline OPA policies |
envoy-org-b |
built from ./envoy (Envoy + Built On Envoy Composer) |
10000, 10001; admin 127.0.0.1:9901 |
Org B gateway; separate ticket-ingress and resource-access JWT + inline OPA policies |
opencode-server |
built from ./opencode-server |
(internal :4096) |
Real OpenCode agent (opencode-ai@1.18.7, headless; Ollama/Anthropic provider) |
opencode-agent |
built from ./opencode-agent |
8100 |
Org A identity harness driving the real OpenCode (task lifecycle) |
triage-agent |
built from ./triage-agent |
(internal only) | Org B remediation agent; reachable from outside cd-net only through Envoy |
sub-agent |
built from ./sub-agent |
8300 |
Org B bounded-privilege agent |
webapp |
built from ./webapp |
8090 |
Animated sequence-diagram demo UI |
jaeger |
jaegertracing/all-in-one:1.65.0 |
16686 |
OpenTelemetry trace backend — collector (OTLP) + UI + storage |
Compose file: cross-domain-id-jag-vc/docker-compose.yaml.