Skip to main content
Version: 0.15 (unstable)

Run the Miden Dashboard UI with Guardian

Stand up a local Guardian server and the operator dashboard UI (0xMiden/guardian-dashboard) side by side with Docker Compose, then log in as an operator and browse accounts end to end.

This is the assembly layer that ties together the existing reference docs — it does not re-explain them. For the dashboard's trust model, the challenge→session auth flow, the allowlist format, and the permission vocabulary, see DASHBOARD.md. For the authoritative meaning of any server variable, see CONFIGURATION.md.

The stack bundles Postgres for Guardian's state and metadata — the published Guardian image is built with the postgres feature, so a database is required (this guide is a realistic self-hosted base, not a throwaway filesystem toy). The ACK signer uses an in-memory key held in a local keystore — fine for a single host; production deployments move it to AWS Secrets Manager / KMS (see the AWS-managed signers guide). The dashboard is a Next.js app the upstream repo ships without an image, so Compose runs it from a source clone on the stock node image.

How the pieces talk: the browser only talks to the dashboard; the dashboard's Next.js backend holds the operator's Falcon private key and signs the challenge→session flow against the Guardian server itself. The server is reached over the Compose network at http://server:3000, so it needs no CORS configuration here.

Prerequisites

  • Docker.
  • A clone of the dashboard inside this guide directory. From here:
    git clone https://github.com/0xMiden/guardian-dashboard
    By default this guide expects it at ./guardian-dashboard. Point elsewhere with GUARDIAN_DASHBOARD_PATH in .env.
  • A free Clerk application — the dashboard uses Clerk for human sign-in and cannot start without its keys.

1. Generate an operator key

The dashboard ships a helper that emits everything you need — the private key and commitment for the dashboard, and the public key for the Guardian allowlist. From your guardian-dashboard clone:

npm install
npx tsx scripts/generate-operator-key.ts

It prints two labelled blocks: a GUARDIAN_OPERATOR_PRIVATE_KEY / GUARDIAN_OPERATOR_COMMITMENT pair, and a ["0x…"] public key. These are not env vars you set directly — in step 3 the private key and commitment go into the privateKey and commitment fields of the GUARDIAN_ENDPOINTS entry in .env, and the public key goes into operators.json. Keep the private key secret. (Falcon keys generated by the multisig SDK or the operator-smoke-web UI work too, as long as you can export all three values.)

2. Configure Clerk

In your Clerk app, copy the test publishable and secret keys for step 3. Then create the admin user (or use your own) and set its public metadata so the dashboard authorises it for this node:

{ "role": "admin", "endpointIds": ["local"] }

endpointIds must include the endpoint id you use in GUARDIAN_ENDPOINTS below (local here). Without it the dashboard signs you in but shows no nodes.

3. Configure the environment

From this directory:

cp .env.example .env
cp operators.example.json operators.json

In .env set:

  • POSTGRES_PASSWORD — a strong, stable, URL-safe value for the bundled Postgres.
  • NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY and CLERK_SECRET_KEY — the test keys from step 2.
  • GUARDIAN_ENDPOINTS — fill commitment and privateKey from step 1. Leave url as http://server:3000 (the server's address on the Compose network) and keep network equal to GUARDIAN_NETWORK_TYPE.

In operators.json replace 0x<your-falcon-operator-pubkey> with the public key from step 1. The default entry grants dashboard:read and accounts:pause; trim the permission set if you only need read access (see the permission vocabulary in DASHBOARD.md). The server hot-reloads this file on every challenge and authenticated request, so you can edit operators without restarting.

4. Run

From this directory (Compose auto-discovers docker-compose.yml and .env):

docker compose up

The first dashboard boot runs npm install inside the container and is slow; subsequent boots reuse the cached node_modules volume. The server starts first; the dashboard only calls it once you sign in, by which point it is ready.

5. Validate

Confirm the server is live and serving its ACK keys:

curl -s localhost:3000/pubkey | jq .

Then open the dashboard at http://localhost:3001, sign in through Clerk, select the Local Guardian endpoint, and confirm the account list loads. A successful list proves the full path: Clerk sign-in → operator challenge→session against the server → an authenticated /dashboard/accounts call.

Troubleshooting

SymptomLikely cause
Sign-in works but no endpoint is selectableClerk public metadata missing endpointIds, or it omits the id from GUARDIAN_ENDPOINTS
Endpoint selected but accounts list is empty or 401sOperator public key not in operators.json, or network in GUARDIAN_ENDPOINTSGUARDIAN_NETWORK_TYPE
Dashboard fails to start citing Clerk keysNEXT_PUBLIC_CLERK_PUBLISHABLE_KEY / CLERK_SECRET_KEY unset in .env
Dashboard container exits / /app is emptyGUARDIAN_DASHBOARD_PATH doesn't point at your guardian-dashboard clone
First docker compose up hangs on the dashboardnpm install running on first boot — wait it out; later boots are fast
Port 3001 already in useStop the conflicting process, or remap the dashboard port in docker-compose.yml

See TROUBLESHOOTING.md for the full server error-code playbook.

Operations checklist

When standing up the dashboard against a stack:

  • At least one operator with dashboard:read in the allowlist — otherwise the dashboard is unreachable.
  • network in GUARDIAN_ENDPOINTS matches GUARDIAN_NETWORK_TYPE.
  • Clerk operator metadata endpointIds includes every endpoint id the user should see.
  • A fresh sign-in → endpoint select → account list round trip succeeds before considering the stack live.