Quickstart¶
Two ways in. The compose stack is the fastest way to see it; the local install is the way to work on it.
With Docker¶
That brings up Keycloak, PostgreSQL and arkhe — split into a minter/admin process and a resolver process, the same way it runs in production — with a ledger already populated.
| URL | |
|---|---|
| Admin UI and minting API | http://localhost:8057/admin/ |
| Resolution (no authentication) | http://localhost:8058/ark:/… |
| API reference | http://localhost:8057/api/docs |
| Keycloak console | http://localhost:8080/ (admin / admin) |
Open http://localhost:8057/admin/ and sign in as one of:
| User | Password | Reach |
|---|---|---|
ops |
arkhe-demo-2026 |
system administrator, every NAAN |
naan-admin |
arkhe-demo-2026 |
NAAN administrator, everything under 99999 |
org-admin |
arkhe-demo-2026 |
organisation administrator, one organisation |
Signing in as each in turn is the quickest way to understand what
reach means: org-admin cannot see the other organisations, and
the audit log answers 403.
Then mint one and resolve it:
TOKEN=$(curl -s -X POST \
http://keycloak.localhost:8080/realms/arkhe/protocol/openid-connect/token \
-d grant_type=client_credentials -d client_id=example-invenio \
-d client_secret=example-invenio-secret-for-demo-only | jq -r .access_token)
ARK=$(curl -s -X POST http://localhost:8057/api/mint \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"url": "https://example.org/records/1", "title": "First object"}' | jq -r .ark)
curl -i "http://localhost:8058/$ARK" # 302 to the URL
curl "http://localhost:8058/$ARK??" # the record and the policy behind it
Stop the authorization server and try both again:
docker compose stop keycloak
# minting is now 401 — resolution still answers 302
docker compose start keycloak
Minting stops; resolution does not. Resolution needs no authentication, so the resolver holds no authentication configuration at all. Identifiers you have already handed out do not become unresolvable because something else broke.
Viewing it from another machine on the LAN¶
Publishing on 0.0.0.0 is not enough on its own. The issuer and the redirect_uri
have to be the URL the browser actually types, so they need a concrete address
(0.0.0.0 is the unspecified address, unusable as a destination; browsers read it as
localhost, so a remote browser would connect to itself).
| Listening on | 0.0.0.0 — accept on any interface |
| issuer | ${ARKHE_DEMO_HOST}:8080 — the URL the browser types |
| redirect_uri | ${ARKHE_DEMO_HOST}:8057 — the string Keycloak matches against |
The redirect_uri is added at startup by lan-redirect. It is not baked into the
realm JSON — that would put one particular LAN address into a published file.
The default (without lan.yml) is 127.0.0.1 because this stack carries its
secrets in the clear and runs Keycloak in dev mode, with the users' passwords
published above. Putting it on a network is a deliberate act.
This stack is for looking at, not for running
The secrets are in the compose file in the clear, Keycloak runs in dev mode, and the demo passwords are published above. See Deployment.
Locally¶
git clone https://github.com/RCOSDP/arkhe.git && cd arkhe
uv venv --python 3.12 && uv pip install -e '.[app,dev]'
python -m pytest -q
Stand up a minimal ledger against SQLite:
export ARKHE_DATABASE_URL="sqlite:///$PWD/arkhe.db"
export ARKHE_AUTH=apikey
alembic upgrade head
arkhe naan add 99999 "Your organisation"
arkhe onboard 99999 "Example University" --shoulder /x9
arkhe client add univ-repo 99999 --manager 1 --scopes "ark:mint ark:update"
arkhe client key univ-repo # the plaintext is shown once and never again
Run it, mint one, resolve it:
uvicorn arkhe.app:create_app --factory &
curl -X POST http://127.0.0.1:8000/api/mint \
-H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
-d '{"url": "https://example.org/records/1", "title": "First object"}'
# → {"ark": "ark:/99999/x9…", …}
curl -i "http://127.0.0.1:8000/ark:/99999/x9…" # 302 to the object
curl "http://127.0.0.1:8000/ark:/99999/x9…??" # the persistence statement
What just happened¶
You minted an identifier that cannot be taken back. ARK declares that names are never re-assigned, so arkhe has no delete: an object that is lost gets a tombstone, not a deletion.
The ?? at the end asked the resolver what it promises about that identifier — a
question you can ask even when the object itself is gone.
Next¶
- What ARK is — the promise the whole design is arranged around
- Authentication — three mechanisms for the API, three ways in for people
- Configuration — every setting