How Hyperledger Identus fits together
Identus is a set of components for building self-sovereign identity systems: agents that hold keys and speak DIDComm, a node that anchors DID operations, and SDKs for edge wallets. This page summarises the moving parts; the official documentation is the authoritative reference.
identus.io/documentationThe stack
A Scala service exposing a REST API for DID management, DIDComm connections, credential issuance and proof presentation. Backed by Postgres and a PRISM node.
source →Anchors DID operations to a ledger (or an in-memory ledger for development) and answers DID resolution requests for did:prism.
source →Stores and forwards DIDComm messages for mobile or intermittently connected wallets that cannot hold an inbound endpoint.
source →Edge-agent SDKs for TypeScript, Kotlin Multiplatform and Swift let wallets hold keys, store credentials and speak DIDComm directly.
source →The credential lifecycle
1 · Create DIDs
The issuer publishes a did:prism with an assertion key; the holder creates one for authentication.
2 · Connect
The issuer creates an out-of-band invitation. The holder accepts it and both sides exchange peer DIDs over DIDComm.
3 · Offer
The issuer sends a credential offer referencing a schema and the claims it will attest.
4 · Accept & issue
The holder accepts the offer; the agent signs a W3C JWT verifiable credential and delivers it to the wallet.
5 · Present & verify
A verifier requests a presentation. The holder responds and the verifier checks signature, issuer DID and revocation status.
Cloud Agent endpoints used by this app
/did-registrar/didsCreate a new managed DID/connectionsCreate a DIDComm out-of-band invitation/issue-credentials/credential-offersOffer a verifiable credential/present-proof/presentationsRequest a presentation from a holder/_system/healthAgent health and versionWhere to run an Identus Cloud Agent
The Cloud Agent ships as container images and needs a Postgres instance with several databases on a private network. That shapes which hosts can run the full stack versus which are only suitable for code snippets.
Use postgres:13-alpine with Cloud Agent 1.40. On Postgres 16 or newer the agent's V27 migration fails with syntax error at or near “format” and the agent never starts — both the Fly deploy and the Compose Lab template pin 13 for that reason.
AgentDelegationCredential for an AI agent, then verify it — right agent, in scope, within the spend cap, not expired — before honouring the action. Signing uses WebCrypto ES256, so the snippets run unchanged in the browser, in Node and in the sandbox, and the claim names match what the x402 gate actually checks.Run Identus with Docker
Docker Compose gives you the highest-fidelity local stack: the same images the hosted deployment uses, on your own machine, with logs and a debugger within reach.
1 · Prerequisites
Docker Desktop or Docker Engine with the Compose v2 plugin — Compose is a docker compose subcommand now, not the old docker-compose binary. The Cloud Agent and PRISM node are JVM services, so give Docker at least 4 GB of memory and 2 CPUs.
docker compose version # expect v2.x docker info | grep -i "total memory"
2 · Get a stack
Either take the upstream stack from the Cloud Agent repository, or generate a validated bundle from the Compose Lab in the console's Sandbox — it writes docker-compose.yml, .env and postgres/init.sql with pinned image tags.
git clone https://github.com/hyperledger-identus/cloud-agent cd cloud-agent/infrastructure/local ./run.sh # agent on http://localhost:8085/cloud-agent
3 · Lifecycle commands
docker compose config # print the interpolated stack, catch .env typos docker compose up -d --wait # start and block until services are healthy docker compose ps # state + published ports docker compose logs -f cloud-agent # follow one service docker compose restart cloud-agent # bounce a single service docker compose pull && docker compose up -d # upgrade to newer image tags docker compose down # stop, keep the Postgres volume docker compose down -v # stop and DELETE all wallet + DID data
--wait is the important one: it returns only once every service with a healthcheck reports healthy, so scripts never race a half-booted agent.
4 · How the stack fits together
your app / console
| REST :8085/cloud-agent DIDComm :8090
v
+--------------+ gRPC :50053 +------------+
| cloud-agent |--------------->| prism-node |
+--------------+ +------------+
| pollux, connect, agent | node
v v
+--------------------------+
| postgres :5432 (pgdata) |
+--------------------------+Four databases are created by postgres/init.sql — pollux (credentials), connect (DIDComm connections), agent (wallets and secrets) and node (PRISM node). Keeping them separate stops the modules' migrations from colliding.
5 · Environment and ports
Only the left side of a port mapping is yours to change. If 5432 is already taken by a local Postgres, set POSTGRES_PORT=5433 — the container keeps listening on 5432 inside the network, so no other service needs editing.
6 · Startup order and health
The agent cannot migrate its schema until Postgres accepts connections, so Postgres declares a pg_isready healthcheck and the other services depend on it with condition: service_healthy. Plain depends_on only waits for the container to start, which is not the same as ready. The agent has its own healthcheck against /_system/health with a generous start_period for JVM boot.
7 · Connect the console
On the Agents page pick Docker local, set the base URL to http://localhost:8085/cloud-agent and paste your ADMIN_TOKEN as the admin API key, then run the health probe. Verify from the shell first:
curl -fsS http://localhost:8085/cloud-agent/_system/health curl -fsS -H "apikey: $ADMIN_TOKEN" \ http://localhost:8085/cloud-agent/did-registrar/dids
8 · Troubleshooting
Error: bind: address already in use
Cause: Another process (often a local Postgres, or a previous stack) already holds that host port.
Fix: Change the host side in .env — e.g. POSTGRES_PORT=5433 — then docker compose up -d --wait. Find the culprit with lsof -i :5432.
cloud-agent restarts in a loop
Cause: Its schema migration failed, usually because a database is missing or credentials changed.
Fix: Read docker compose logs cloud-agent for the Flyway/JDBC error, confirm all four databases exist with docker compose exec postgres psql -U postgres -l, then reset with docker compose down -v.
Databases missing even though init.sql is present
Cause: Scripts in /docker-entrypoint-initdb.d only run when the data directory is empty, and the pgdata volume already existed.
Fix: docker compose down -v to drop the volume, then bring the stack back up so the init script runs.
no matching manifest for linux/arm64
Cause: The pinned image has no arm64 build (common on Apple Silicon).
Fix: Add platform: linux/amd64 to that service and expect emulation to be slower, or pick a tag that publishes multi-arch images.
pull access denied / unauthorized
Cause: The tag points at a private or non-existent registry path.
Fix: Use the public Docker Hub images identus/identus-cloud-agent and identus/prism-node with an explicit version tag — never :latest.
Agent healthy but the console cannot reach it
Cause: The base URL is missing the /cloud-agent prefix, or the apikey header is not being sent.
Fix: Use http://localhost:8085/cloud-agent and set the admin key to your ADMIN_TOKEN value, then re-run the health probe.
9 · Data and cleanup
All state lives in the named volume pgdata. Published DIDs and issued credentials do not survive a down -v, so back it up before resetting.
docker compose exec postgres pg_dumpall -U postgres > identus-backup.sql docker volume ls | grep pgdata docker compose down -v && docker compose up -d --wait # clean slate
Other ways to run
Fly.io, from the console
The Agents page provisions a Fly app with three machines — Postgres, a PRISM node and the Cloud Agent — using your Fly organisation token, then stores the generated API key so the console can talk to it over HTTPS.
Simulated
No infrastructure. The app performs the same workflow deterministically so you can learn the protocol shape before deploying anything.