Identity & access
IAM is included in Lupid as one capability — not the whole product. If full IAM is the only thing you need, you can run Lupid for just that and ignore everything else. Most deployments use the runtime protection and the audit trail too, but the choice is yours.
Two principals: agents and operators
Section titled “Two principals: agents and operators”| Principal | What they are | How they authenticate |
|---|---|---|
| Agent | A non-human consumer of policy decisions (an LLM agent, a script) | Ed25519 keypair + JWT session |
| Operator | A human using the dashboard or CLI | Email + bcrypt-hashed password (via NextAuth) and HMAC header to the API |
Agent identity
Section titled “Agent identity”Each agent registered with Lupid gets:
- An Ed25519 keypair. The private key is encrypted at rest in Postgres with
AES-256-GCM (key from
AGENTUM_ENCRYPTION_KEY_HEX). - A signed passport. The public key plus the agent metadata, signed by the server’s signing key.
- A JWT session token scoped to the agent’s network context (source IP can be bound at issue time).
The agent presents the JWT on every request. Lupid’s agentum-authn crate verifies
the signature, looks the token up in the revocation blocklist, and reads the agent’s
declared tools to seed the policy evaluation context.
Operator identity
Section titled “Operator identity”Operators log into the dashboard with email + password. The password is bcrypt-hashed
server-side. The session is a NextAuth-encrypted JWT cookie sealed with
NEXTAUTH_SECRET.
Every API call the dashboard makes carries an HMAC-SHA256 header
(X-Agentum-Operator) signed with AGENTUM_OPERATOR_SECRET_HEX. The HMAC payload
includes a 5-minute time window so a stolen header expires quickly.
Bootstrap
Section titled “Bootstrap”On the very first start, when the operators table is empty and
AGENTUM_ADMIN_EMAIL and AGENTUM_ADMIN_PASSWORD are both set in .env, Lupid
creates exactly one admin operator from those values. The password is bcrypt-hashed
in-process; the plaintext is never written to disk past the .env.
Subsequent boots log skipping initial-admin bootstrap and never overwrite the row.
After first boot, create additional operators from the dashboard’s Settings → Users
page or via agentum operator create.
Three roles, in order of increasing privilege:
| Role | Can read | Can write |
|---|---|---|
viewer | everything | nothing |
operator | everything | ack / resolve alerts, decide HITL requests, kill agents |
admin | everything | the above + mint API keys, register agents, edit policies, manage operator accounts (create / update / delete) |
Mint an admin key for full operator capability. The HMAC bootstrap payload signs
with admin for the same reason — it’s the top of the hierarchy.
The kill switch
Section titled “The kill switch”Every agent has a state machine. Operators can transition with one click:
Provisioning → Active → Suspended → Quarantined → Decommissioned- Active. Normal operation. JWTs verify, policy evaluates, calls flow.
- Suspended. JWT added to the revocation blocklist. The agent’s next request fails authentication. Reversible.
- Quarantined. Suspended plus every audit event from this agent is tagged for investigator review. Use when you suspect compromise.
- Decommissioned. Terminal state. The agent is archived; new requests are
rejected with
agent_not_found.
Revocation propagates via Postgres LISTEN/NOTIFY across every Lupid replica in the
cluster, so the kill is effective everywhere within ~100 ms. The blocklist itself is
an XOR8-style filter in memory backed by the revocation_blocklist table.