The security layer
The security layer is what makes Lupid more than an audit log. Every request your agent makes passes through it, and the layer can block, redact, defer, or allow each one based on rules you write.
Policy enforcement
Section titled “Policy enforcement”Every tool call is evaluated against a declarative policy attached to the agent. The engine returns one of three outcomes:
- allow — the call proceeds, the audit row notes the policy id and
allow. - deny — the call never leaves Lupid; agent sees
HTTP 403. - require_hitl — the call parks in the approval queue (see below) and only goes through once a human approver clicks through.
Policies live per-agent. The engine evaluates against (principal, action, resource)
where principal is the agent, action is the tool name, and resource is whatever
the tool is touching (a URL, a file path, a database).
A policy that allows the agent to call any tool but require_hitl on bash looks like:
permit(principal == Agentum::Agent::"<agent-uuid>", action, resource);
@advice("require_hitl:bash")permit( principal == Agentum::Agent::"<agent-uuid>", action == Action::"call_tool", resource is Agentum::Tool) when { resource.name == "bash" };See Writing a policy for the full walk-through.
In-line DLP
Section titled “In-line DLP”DLP scans the request body before the request leaves Lupid, and the response body before it returns to the agent. The detector ships seventeen kinds:
| Category | Detectors |
|---|---|
| PII | Email, PhoneNumber, SsnUs, CreditCard, IpAddress |
| API keys | AwsAccessKeyId, AwsSecretAccessKey, GithubToken, SlackToken, OpenAiKey, AnthropicKey, GoogleApiKey |
| Auth tokens | JwtToken, BearerToken |
| Cryptographic | PrivateKeyPem |
| Heuristic | GenericHighEntropy |
For each detector, you set a per-severity action. Lupid maps each detector to one of
Low, Medium, High, Critical. The action options are:
observe— log the hit, don’t block, don’t redact.warn— log the hit, emit an alert, but let the call through.redact— replace the matched substring with a tag like[EMAIL]before the request leaves (or before the response reaches the agent).block— drop the request entirely; agent gets a403.
The defaults ship safe: block at High and Critical, warn at Medium, observe
at Low. Override via the AGENTUM_DLP_ACTION_HIGH / AGENTUM_DLP_ACTION_CRITICAL
env vars.
Credential vault
Section titled “Credential vault”Agent credentials never live in the agent’s environment as long-lived static values. Operators store them once in the vault; agents request short-lived leases at the moment they need to use them.
The vault supports four backends, picked by AGENTUM__VAULT_BACKEND:
static(default) — encrypted at rest in thecredentialsPostgres column with AES-256-GCM, key derived fromAGENTUM_ENCRYPTION_KEY_HEX.hashicorp— HashiCorp Vault KV v2 mount.aws— AWS Secrets Manager.azure— Azure Key Vault.
A lease workflow:
- Operator stores the underlying credential:
agentum vault store --agent-id <id> --service-name stripe --from-env STRIPE_KEY. - Agent (or operator on its behalf) requests a lease:
agentum vault issue --agent-id <id> --service-name stripe. The response includes the plaintext value and anexpires_attimestamp. - The agent uses the value directly, or — on the proxy path — the agent puts the
placeholder
Authorization: Bearer agentum://stripe-keyin its request and the gateway substitutes the real value before the request leaves. - The lease auto-expires. Revoke immediately with
agentum vault revoke <lease-id>, or revoke every lease for an agent (e.g. on suspension) viaPOST /api/v1/vault/revoke-agent/<agent-id>.
See Credential vault for the full walk-through.
Human-in-the-loop approvals
Section titled “Human-in-the-loop approvals”Some tool calls are too consequential to enforce purely by policy — buying inventory,
sending production email, deleting customer data. Lupid lets you tag those with
@advice("require_hitl:<reason>") in the policy. When a matching call comes through:
- The request parks in the
approval_requeststable with statepending. - An HMAC-signed webhook fires to every configured webhook URL. The payload has the agent id, tool, resource, reason, and a deep link into the dashboard.
- The agent either blocks synchronously (long-poll) or gets a
202with a poll URL. - An operator with the right role clicks Approve or Deny on the HITL page.
- The request proceeds (or doesn’t) and an audit event is written either way.
You can require multiple approvers per request (set required_approvals on the
policy advice). The queue dedupes pending requests on dedup_key.
Kill switch
Section titled “Kill switch”Every agent has a state machine: Provisioning → Active → Suspended → Quarantined → Decommissioned.
You can move an agent between states from the dashboard or via
agentum agent {suspend,quarantine,kill,activate}.
Suspended adds the agent’s JWT to the revocation blocklist. The blocklist is a
bloom-filtered list backed by Postgres, distributed across replicas via LISTEN/NOTIFY.
Within ~100 ms the kill propagates everywhere. Quarantined goes further and tags every
audit event from the agent for investigator review.