# Core Concepts

Everything you need to understand how SentryAgent.ai AgentIdP works — without needing to read an RFC.

---

## What is AgentIdP?

SentryAgent.ai AgentIdP is a free, open-source Identity Provider (IdP) built specifically for AI agents. It answers three questions that today's auth systems don't handle for agents:

1. **Who is this agent?** — a unique, immutable identity registered in the AgentIdP registry
2. **Is it who it claims to be?** — verified via OAuth 2.0 credentials
3. **Is it allowed to do this?** — enforced via scope-based access control

Think of it as the difference between a human logging in with a password and a service account authenticating with client credentials. Humans use passwords and MFA. Agents use `client_id` + `client_secret` — and AgentIdP manages that for them.

---

## What is an AI Agent Identity?

A human identity has a username, a password, and a profile. An AI agent identity has the equivalent:

| Human | AI Agent |
|-------|----------|
| Username | `email` (unique identifier, e.g. `screener-001@myproject.ai`) |
| Immutable ID | `agentId` (UUID, assigned at registration, never changes) |
| Profile | `agentType`, `version`, `capabilities`, `owner`, `deploymentEnv` |
| Password | `clientSecret` (generated, stored as bcrypt hash) |
| Login session | JWT access token (1 hour, RS256 signed) |
| Account status | `status` (active / suspended / decommissioned) |

The key difference from human identities: an agent's `agentId` is **immutable**. Once assigned, it never changes — even if other metadata is updated. This makes it safe to use as a stable reference across systems.

Agents also carry **capabilities** — a list of `resource:action` strings (e.g. `resume:read`, `email:send`) that describe what the agent is permitted to do. These are informational in Phase 1 and will be enforced in the authorization layer in Phase 2.

---

## AGNTCY Alignment

AGNTCY is an open standard from the Linux Foundation that defines how AI agents should be identified, authenticated, and governed across different systems and platforms.

The key principle: **agents are first-class identities**, not service accounts bolted onto human auth systems.

What this means for you as a developer:

- Your agent gets its own permanent ID that travels with it across systems
- Other AGNTCY-compliant systems can verify your agent's identity without trusting your word
- Your agent's full lifecycle — registration, credential rotation, decommission — follows a defined, interoperable model

SentryAgent.ai implements AGNTCY's non-human identity model. When you register an agent here, you're registering it in a way that aligns with where the industry is heading, not a proprietary silo.

---

## Agent Lifecycle

Every agent moves through a defined set of states. Understanding these states matters because they affect whether your agent can authenticate.

### States

| State | What it means | Can get tokens? | Can be updated? |
|-------|---------------|-----------------|-----------------|
| `active` | Agent is operational | Yes | Yes |
| `suspended` | Temporarily disabled | No — credentials rejected | Yes — can be reactivated |
| `decommissioned` | Permanently retired | No — credentials revoked | No |

### Transitions

```
registration
     |
     v
  [active] <-----> [suspended]
     |
     v (irreversible)
[decommissioned]
```

**Suspending** an agent prevents it from obtaining new tokens. Existing unexpired tokens continue to work until they expire. Use suspension when you need to temporarily disable an agent (e.g. investigation, maintenance).

**Decommissioning** an agent permanently retires it. All active credentials are immediately revoked. The agent record is retained in the database for audit purposes but the agent can never be reactivated. This operation is **irreversible** — use it only when you intend to permanently retire the agent.

---

## OAuth 2.0 Client Credentials

OAuth 2.0 is the auth standard used everywhere — GitHub, Google, Stripe. AgentIdP uses one specific flow from OAuth 2.0: the **Client Credentials grant**.

Here is what actually happens when your agent authenticates:

1. Your agent has a `client_id` (its `agentId`) and a `client_secret` (generated by AgentIdP)
2. Your agent sends both to `POST /token` along with the scopes it needs
3. AgentIdP verifies the secret, checks the agent is active, and issues a **JWT access token**
4. Your agent attaches that token as `Authorization: Bearer <token>` on all subsequent API calls
5. The token expires after 1 hour — your agent requests a new one

There are no redirects, no browser windows, no user consent screens. It is a direct machine-to-machine exchange — exactly right for agents that run unattended.

### Scopes

Scopes limit what a token is permitted to do. Request only the scopes your agent actually needs.

| Scope | What it allows |
|-------|----------------|
| `agents:read` | Read agent identity records |
| `agents:write` | Create, update, and decommission agent records |
| `tokens:read` | Introspect tokens (check if active/expired) |
| `audit:read` | Query the audit log |

Example: an agent that only reads audit logs should request only `audit:read`. If it doesn't have `agents:write`, it cannot accidentally modify agent records.

### The secret is shown once

When you generate credentials (`POST /agents/{agentId}/credentials`), the `clientSecret` is returned in the response **one time only**. AgentIdP stores a bcrypt hash — the plaintext is gone. If you lose the secret, you rotate the credential to get a new one.

---

## Free Tier Limits

AgentIdP is free. These are the limits on the free tier:

| Resource | Limit | What happens when exceeded |
|----------|-------|---------------------------|
| Registered agents | 100 | `POST /agents` returns `403 FREE_TIER_LIMIT_EXCEEDED` |
| Token requests/month | 10,000 | `POST /token` returns `403 unauthorized_client` |
| API rate limit | 100 req/min | All endpoints return `429 RATE_LIMIT_EXCEEDED` with `X-RateLimit-*` headers |
| Audit log retention | 90 days | Events older than 90 days are automatically purged; queries return empty results |

The monthly token counter resets on the first day of each calendar month. The rate limit window resets every 60 seconds; the reset timestamp is in the `X-RateLimit-Reset` response header.

---

## Organizations and Multi-tenancy

An **organization** is the top-level grouping unit in AgentIdP. Every registered agent can be
scoped to an organization by including an `organization_id` in the agent registration request.
Organizations have a unique `slug` (URL-safe identifier), a display `name`, and a `planTier`
that controls per-org resource limits. All API operations that involve analytics, webhooks, tiers,
and delegation are tenant-scoped: they only see data belonging to their organization.

**Tenant isolation** is enforced at the service layer. Every query involving multi-tenant data
filters by `organization_id`. A token issued to an agent in org A cannot read data from org B.
The `organization_id` is embedded in the JWT at token issuance time and validated on every
request. This means you do not need to pass an org ID as a query parameter — it is derived
automatically from the authenticated token.

When you create an organization, you define its `slug`. Slugs are immutable — once set, they
cannot be changed. Choose a slug that matches your domain or product namespace, as it is used
in DID identifiers for agents in that organization. Membership is managed through the
`POST /api/v1/organizations/{orgId}/members` endpoint, which lets you add an existing agent
to an organization with a `member` or `admin` role.

| Field | Type | Description |
|-------|------|-------------|
| `organizationId` | UUID | System-assigned immutable identifier |
| `name` | string | Human-readable display name |
| `slug` | string | URL-safe unique identifier (immutable after creation) |
| `planTier` | enum | `free` \| `pro` \| `enterprise` |
| `maxAgents` | integer | Maximum active agents in this org |
| `maxTokensPerMonth` | integer | Maximum token issuances per month |
| `status` | enum | `active` \| `suspended` \| `deleted` |

---

## DID Identity

Every agent registered in AgentIdP automatically receives a **Decentralized Identifier (DID)**
using the `did:web` method. A DID is a globally unique, self-describing identifier that does not
rely on a central registry. The DID for an agent takes the form
`did:web:<host>:agents:<agentId>` — for example,
`did:web:localhost%3A3000:agents:a1b2c3d4-e5f6-7890-abcd-ef1234567890`. The `did:web` method
means the DID document is resolvable via HTTPS: a resolver fetches
`https://<host>/api/v1/agents/<agentId>/did`.

The **DID Document** is a JSON-LD object that describes the agent's cryptographic keys and
service endpoints. It contains: the agent's DID as its `id`, a `verificationMethod` array with
the agent's public key in JWK format, an `authentication` array referencing that key, and an
`agntcy` extension object carrying agent metadata (type, capabilities, version, owner,
deploymentEnv). This document is publicly accessible — no authentication required — so any
external system can verify this agent's identity without contacting AgentIdP directly.

The `did:web` scheme was chosen because it is widely supported by DID resolvers, requires no
blockchain, and leverages standard HTTPS infrastructure. When an external system receives a
token from your agent, it can resolve your agent's DID, retrieve the public key from the DID
Document, and independently verify the token's signature. This is the foundation of
cross-system agent identity verification.

```
DID Document structure for a registered agent
───────────────────────────────────────────────
{
  "@context": ["https://www.w3.org/ns/did/v1"],
  "id": "did:web:<host>:agents:<agentId>",
  "controller": "did:web:<host>:agents:<agentId>",
  "verificationMethod": [
    {
      "id": "<did>#key-1",
      "type": "JsonWebKey2020",
      "controller": "<did>",
      "publicKeyJwk": { "kty": "RSA", ... }
    }
  ],
  "authentication": ["<did>#key-1"],
  "agntcy": {
    "agentId": "<uuid>",
    "agentType": "screener",
    "capabilities": ["resume:read"],
    "deploymentEnv": "production",
    "owner": "talent-team",
    "version": "1.0.0"
  }
}
```

---

## OIDC Provider

AgentIdP implements a subset of the **OpenID Connect (OIDC)** protocol, acting as an OIDC
Provider for the agents it manages. This means AgentIdP publishes a standard discovery
document at `GET /.well-known/openid-configuration`, which any OIDC-aware client can use to
discover supported grant types, token endpoint, JWKS URI, and other metadata. It also exposes
a JWKS endpoint at `GET /.well-known/jwks.json` for external systems to retrieve the public
keys used to verify tokens.

The **`/agent-info` endpoint** is the equivalent of OIDC's UserInfo endpoint — it returns
identity claims for the authenticated agent. External systems that receive a token issued by
AgentIdP can call this endpoint (with that token) to retrieve the agent's verified identity
attributes: its `agentId`, `email`, `agentType`, `capabilities`, and `organization_id`. This
is particularly useful when a downstream service needs to verify the identity of an agent
presenting a token, without duplicating identity data in its own store.

AgentIdP also supports **OIDC token exchange for GitHub Actions**. If you run your agent
deployment workflows in GitHub Actions, you can configure a trust policy
(`POST /api/v1/oidc/trust-policies`) that maps a GitHub repository and branch to an AgentIdP
agent. The workflow can then exchange its GitHub OIDC JWT for an AgentIdP access token via
`POST /api/v1/oidc/token` — no stored secrets required. This enables keyless, short-lived
token issuance in CI/CD pipelines.

---

## A2A Delegation

**Agent-to-Agent (A2A) delegation** allows one agent to grant another agent a subset of its own
OAuth 2.0 scopes for a limited time. This is the building block for multi-agent pipelines where
an orchestrator agent needs to delegate work to a specialist sub-agent without sharing its own
full credentials. A delegation chain consists of: a delegator (the agent granting authority),
a delegatee (the agent receiving authority), a set of scopes (must be a strict subset of the
delegator's own scopes), and a TTL (60 seconds to 86,400 seconds).

The **grant flow** is straightforward: the delegator calls `POST /api/v1/oauth2/token/delegate`
with the delegatee's agent ID, the scopes to grant, and the TTL. AgentIdP returns a signed
delegation token. The delegatee presents this token when calling
`POST /api/v1/oauth2/token/verify-delegation` to prove it has been granted authority. AgentIdP
verifies the chain integrity and returns the delegation details including whether it is still
valid. The delegator can revoke the chain at any time via
`DELETE /api/v1/oauth2/token/delegate/{chainId}`.

Delegation is useful for: workflow handoffs between specialist agents, granting a monitoring
agent read-only access to resources owned by a processing agent, and time-limited cross-agent
authorization without credential sharing. Because delegation tokens are signed and verified
server-side, a delegatee cannot extend the TTL, expand the scope, or pass the delegation to a
third agent. The chain is always exactly two hops: delegator → delegatee.

```
A2A Delegation Flow
───────────────────
1. Orchestrator (delegator) calls POST /api/v1/oauth2/token/delegate
   → body: { delegateeAgentId, scopes: ["agents:read"], ttlSeconds: 3600 }
   ← response: { delegationToken: "...", chainId: "...", expiresAt: "..." }

2. Orchestrator passes delegationToken to the sub-agent out-of-band

3. Sub-agent (delegatee) calls POST /api/v1/oauth2/token/verify-delegation
   → body: { delegationToken: "..." }
   ← response: { valid: true, scopes: ["agents:read"], expiresAt: "..." }

4. Sub-agent uses its own Bearer token + confirmed scope to act on behalf

5. (Optional) Orchestrator calls DELETE /api/v1/oauth2/token/delegate/{chainId}
   to revoke early
```

---

## API Tier Plans

AgentIdP has three subscription tiers: **Free**, **Pro**, and **Enterprise**. Every organization
is on one tier at a time. The tier determines the resource limits enforced at runtime: maximum
number of active agents, maximum API calls per day, and maximum token issuances per day. When a
limit is reached, the relevant operation returns a `403 FREE_TIER_LIMIT_EXCEEDED` error until the
next calendar day resets the counter (for daily limits) or until you upgrade your tier.

You can check your current tier, configured limits, and live usage at any time by calling
`GET /api/v1/tiers/status`. The response shows your tier name, all three limit values, and the
live usage counters for the current day. If you need higher limits, call
`POST /api/v1/tiers/upgrade` with `{ "target_tier": "pro" }` or `"enterprise"`. This creates a
Stripe Checkout Session and returns a one-time `checkoutUrl`. After payment, the organization's
tier is updated automatically via Stripe webhook.

Enterprise tier limits are effectively unlimited (enforced as `Infinity` in the tier
configuration). Enterprise customers should contact SentryAgent.ai to arrange billing and
configure custom limits if needed. The `maxAgents` and `maxTokensPerMonth` fields on an
organization record can be overridden at org creation or update to set tighter or looser limits
than the tier defaults, regardless of tier.

| Limit | Free | Pro | Enterprise |
|-------|------|-----|------------|
| Max agents | 10 | 100 | Unlimited |
| Max API calls / day | 1,000 | 50,000 | Unlimited |
| Max token issuances / day | 1,000 | 50,000 | Unlimited |
| Audit log retention | 90 days | 90 days | 90 days |
| Webhooks | Yes | Yes | Yes |
| Analytics | Yes | Yes | Yes |
| A2A Delegation | Yes | Yes | Yes |

---

## AGNTCY Compliance

**AGNTCY** is an open standard from the Linux Foundation that defines how AI agents should be
identified, described, and governed across platforms. AgentIdP implements AGNTCY compliance
in two ways: every agent automatically gets a DID and an agent card (a structured JSON object
that describes the agent in the AGNTCY format), and AgentIdP can generate a **compliance
report** that summarizes the verified state of all agents in a tenant. An agent card is the
AGNTCY equivalent of a business card — it carries the agent's DID, type, capabilities, owner,
version, and identity provider.

The **compliance report** (available at `GET /api/v1/compliance/report`) covers two dimensions:
agent-identity verification (are all active agents reachable via their DID?) and audit-trail
integrity (is the hash chain of audit events intact?). The report includes a boolean
`agntcyConformance` field that summarizes whether the tenant meets AGNTCY baseline requirements.
Reports are cached in Redis for 5 minutes; the `X-Cache: HIT` header signals a cached response.

For self-auditing and external audits, you can export all active agents as AGNTCY agent cards
in bulk via `GET /api/v1/compliance/agent-cards`. This is an array of card objects that
external compliance tools and AGNTCY-compatible registries can ingest directly. The
`GET /api/v1/compliance/controls` endpoint (no authentication required) provides a live
status snapshot of all SOC 2 Trust Services Criteria controls that AgentIdP monitors internally.
These endpoints are gated by the `COMPLIANCE_ENABLED` environment variable; if disabled, they
return `404`.
