docs: bedroom developer documentation — complete docs/developers/ set
Adds the full bedroom-developer-docs OpenSpec change implementation: - docs/developers/README.md — index page - docs/developers/quick-start.md — bootstrap to working token in 7 steps - docs/developers/concepts.md — AgentIdP, AGNTCY, lifecycle, OAuth 2.0, free tier - docs/developers/guides/README.md — guide index - docs/developers/guides/register-an-agent.md — all fields, validation, common errors - docs/developers/guides/manage-credentials.md — generate, list, rotate, revoke - docs/developers/guides/issue-and-revoke-tokens.md — OAuth 2.0 flow, introspect, revoke - docs/developers/guides/query-audit-logs.md — filters, pagination, 90-day retention - docs/developers/api-reference.md — all 14 endpoints, all error codes, curl examples Also commits deferred OpenSpec housekeeping from previous session: - Archives phase-1-mvp-implementation change to openspec/changes/archive/ - Adds bedroom-developer-docs change artifacts (30/30 tasks complete) - Syncs 4 delta specs to openspec/specs/ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
183
docs/developers/guides/query-audit-logs.md
Normal file
183
docs/developers/guides/query-audit-logs.md
Normal file
@@ -0,0 +1,183 @@
|
||||
# Query Audit Logs
|
||||
|
||||
The audit log is an immutable, append-only record of every significant action on the AgentIdP platform. This guide covers how to query it, what filters are available, and how retention works.
|
||||
|
||||
Requires: `Authorization: Bearer <token>` with `audit:read` scope.
|
||||
|
||||
---
|
||||
|
||||
## What gets logged
|
||||
|
||||
Every action below is automatically recorded. You cannot create, modify, or delete audit events — the log is read-only via the API.
|
||||
|
||||
| Action | Triggered by |
|
||||
|--------|-------------|
|
||||
| `agent.created` | Successful `POST /agents` |
|
||||
| `agent.updated` | Successful `PATCH /agents/{agentId}` |
|
||||
| `agent.decommissioned` | Successful `DELETE /agents/{agentId}` |
|
||||
| `agent.suspended` | Status changed to `suspended` |
|
||||
| `agent.reactivated` | Status changed from `suspended` to `active` |
|
||||
| `token.issued` | Successful `POST /token` |
|
||||
| `token.revoked` | Successful `POST /token/revoke` |
|
||||
| `token.introspected` | Successful `POST /token/introspect` |
|
||||
| `credential.generated` | Successful `POST /agents/{agentId}/credentials` |
|
||||
| `credential.rotated` | Successful `POST /agents/{agentId}/credentials/{credentialId}/rotate` |
|
||||
| `credential.revoked` | Successful `DELETE /agents/{agentId}/credentials/{credentialId}` |
|
||||
| `auth.failed` | Failed authentication attempt on `POST /token` |
|
||||
|
||||
---
|
||||
|
||||
## Query the audit log
|
||||
|
||||
`GET /api/v1/audit`
|
||||
|
||||
Returns a paginated list of audit events, most recent first.
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:3000/api/v1/audit" \
|
||||
-H "Authorization: Bearer $TOKEN" | jq .
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"eventId": "f1e2d3c4-b5a6-7890-cdef-123456789012",
|
||||
"agentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
||||
"action": "token.issued",
|
||||
"outcome": "success",
|
||||
"ipAddress": "127.0.0.1",
|
||||
"userAgent": "curl/7.88.1",
|
||||
"metadata": {
|
||||
"scope": "agents:read agents:write",
|
||||
"expiresAt": "2026-03-28T10:00:00.000Z"
|
||||
},
|
||||
"timestamp": "2026-03-28T09:00:00.000Z"
|
||||
}
|
||||
],
|
||||
"total": 47,
|
||||
"page": 1,
|
||||
"limit": 50
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Audit event structure
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `eventId` | UUID | Immutable unique ID for this event |
|
||||
| `agentId` | UUID | The agent that triggered the event |
|
||||
| `action` | string | What happened (see table above) |
|
||||
| `outcome` | string | `success` or `failure` |
|
||||
| `ipAddress` | string | Client IP (IPv4 or IPv6) |
|
||||
| `userAgent` | string | HTTP User-Agent from the request |
|
||||
| `metadata` | object | Action-specific details (varies by action) |
|
||||
| `timestamp` | ISO 8601 | When the event occurred |
|
||||
|
||||
### `metadata` by action
|
||||
|
||||
| Action | Metadata fields |
|
||||
|--------|----------------|
|
||||
| `token.issued` | `scope`, `expiresAt` |
|
||||
| `credential.generated` | `credentialId` |
|
||||
| `credential.rotated` | `credentialId` |
|
||||
| `agent.created` | `agentType`, `owner` |
|
||||
| `auth.failed` | `reason`, `clientId` |
|
||||
|
||||
---
|
||||
|
||||
## Filters
|
||||
|
||||
All filter parameters are optional and can be combined (logical AND).
|
||||
|
||||
### Filter by agent
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:3000/api/v1/audit?agentId=$AGENT_ID" \
|
||||
-H "Authorization: Bearer $TOKEN" | jq .
|
||||
```
|
||||
|
||||
### Filter by action
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:3000/api/v1/audit?action=token.issued" \
|
||||
-H "Authorization: Bearer $TOKEN" | jq .
|
||||
```
|
||||
|
||||
### Filter by outcome
|
||||
|
||||
```bash
|
||||
# Failed authentication attempts only
|
||||
curl -s "http://localhost:3000/api/v1/audit?outcome=failure" \
|
||||
-H "Authorization: Bearer $TOKEN" | jq .
|
||||
```
|
||||
|
||||
### Filter by date range
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:3000/api/v1/audit?fromDate=2026-03-01T00:00:00.000Z&toDate=2026-03-28T23:59:59.999Z" \
|
||||
-H "Authorization: Bearer $TOKEN" | jq .
|
||||
```
|
||||
|
||||
### Combine filters
|
||||
|
||||
```bash
|
||||
# All failed token requests for a specific agent today
|
||||
curl -s "http://localhost:3000/api/v1/audit?agentId=$AGENT_ID&action=auth.failed&outcome=failure&fromDate=2026-03-28T00:00:00.000Z" \
|
||||
-H "Authorization: Bearer $TOKEN" | jq .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pagination
|
||||
|
||||
Default page size is 50, maximum is 200.
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:3000/api/v1/audit?page=2&limit=100" \
|
||||
-H "Authorization: Bearer $TOKEN" | jq .
|
||||
```
|
||||
|
||||
Use `total`, `page`, and `limit` from the response to calculate the number of pages:
|
||||
|
||||
```
|
||||
total_pages = ceil(total / limit)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Get a single event
|
||||
|
||||
`GET /api/v1/audit/{eventId}`
|
||||
|
||||
```bash
|
||||
curl -s "http://localhost:3000/api/v1/audit/$EVENT_ID" \
|
||||
-H "Authorization: Bearer $TOKEN" | jq .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Retention — 90 days
|
||||
|
||||
On the free tier, audit events are retained for 90 days. Events older than 90 days are automatically purged.
|
||||
|
||||
- Querying for dates outside the 90-day window returns an empty result set — not an error
|
||||
- Requesting a specific `eventId` for a purged event returns `404 Not Found`
|
||||
- The `fromDate` filter cannot be set to a date older than 90 days; doing so returns `400 RETENTION_WINDOW_EXCEEDED`
|
||||
|
||||
To check the earliest available date:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "RETENTION_WINDOW_EXCEEDED",
|
||||
"message": "Free tier audit log retention is 90 days. Requested date is outside the retention window.",
|
||||
"details": {
|
||||
"retentionDays": 90,
|
||||
"earliestAvailable": "2025-12-28T00:00:00.000Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user