API keys & encryption setup
From dashboard sign-in to events flowing, and what's happening under the hood at each step.
Connecting an application to Guard Core takes eight steps. The first six happen in the dashboard. The last two happen in your app. Once you know where things live, the whole flow takes about five minutes.
The flow, end to end
| Step | Surface | What you do |
|---|---|---|
| 1 | Dashboard | Sign in at app.guard-core.com |
| 2 | Dashboard | Create or open a Project (or use the Demo project) |
| 3 | Dashboard | Copy the Project ID - format proj_ + 16 alphanumeric chars |
| 4 | Dashboard | Open API Keys (from the project page link, or Settings → API Keys) |
| 5 | Dashboard | Create a key. Enable encryption (recommended). |
| 6 | Dashboard | Copy both credentials - shown ONCE, no recovery path |
| 7 | Your app | Install the adapter + telemetry agent: pip install fastapi-guard guard-agent (or uv add fastapi-guard guard-agent) |
| 8 | Your app | Paste into SecurityConfig in app.py, then restart. Events start flowing. |
What a project is
A project is the scoping unit for everything Guard Core does. Events, metrics, threats, rules, and API keys are all scoped to a project. The common pattern is one app per project, but one project per team / service / environment also works.
For current per-tier limits and quotas, check Settings → Billing in the dashboard. They vary by plan and change.
Project ID format
Externally: proj_ prefix followed by 16 alphanumeric characters. Internally the project is a UUIDv7. The API accepts both forms for routing and scoping. They map to the same row.
What an API key actually is
At rest, the core stores only the SHA-256 hash of the key, never the plaintext. Verification uses a timing-safe comparison so a leaked database doesn't give an attacker usable keys.
Lifecycle: the plaintext exists in one HTTP response and nowhere else. Lost it? Revoke and create a new one. There's no recovery endpoint.
POST /api/v1/events
X-API-Key: fg_01He...
X-Project-Id: proj_abc123def456...Encryption - AES-256-GCM, end to end
When you enable encryption at key-creation time, the project gets a second secret: a project encryption key. The agent uses it to seal every event before it leaves your process.
| Property | Value |
|---|---|
| Algorithm | AES-256-GCM (authenticated) |
| Nonce | 12 bytes, random per request |
| Authentication tag | 16 bytes |
| Transport encoding | Base64 |
| Scope | The entire JSON event payload, not per-field |
| Layering | Sits on top of TLS - useful when a TLS-terminating proxy is in the path |
| At rest | If the project has an encryption key, ingested events are persisted encrypted |
Project key lifecycle
- Created - random 32-byte value generated server-side, returned to you once (base64).
- Stored (server) - wrapped under a server-side master key.
- Held (client) - you set it as
agent_project_encryption_keyinSecurityConfig; the agent uses it to seal events. - Rotation - call the rotation endpoint. The old wrapped key is replaced. Historical events stay decryptable because each ciphertext carries its own version marker; new events use the new version.
The ingest endpoints
The agent picks the right endpoint automatically based on your config.
| Endpoint | Used when |
|---|---|
POST /api/v1/events | No project encryption key configured |
POST /api/v1/events/encrypted | agent_project_encryption_key is set |
POST /api/v1/status | Agent heartbeat - liveness + buffer stats |
POST /api/v1/metrics | Aggregated counters and timings |
Wiring it into SecurityConfig
| Dashboard value | → | SecurityConfig field |
|---|---|---|
| API key | → | agent_api_key |
| Project ID | → | agent_project_id |
| Encryption key (if enabled) | → | agent_project_encryption_key |
| Core URL | → | agent_endpoint (e.g., https://api.guard-core.com) |
| Turn on reporting | → | enable_agent=True |
from fastapi import FastAPI
from guard import SecurityConfig, SecurityMiddleware
from guard.lifespan import guard_lifespan
security_config = SecurityConfig(
enable_agent=True,
agent_api_key=API_KEY,
agent_project_id=PROJECT_ID,
agent_endpoint="https://api.guard-core.com",
agent_project_encryption_key=ENC_KEY,
agent_buffer_size=5000,
agent_flush_interval=2,
)
app = FastAPI(lifespan=guard_lifespan)
app.add_middleware(SecurityMiddleware, config=security_config)Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Agent requests return 401 | Wrong key, revoked, or expired | Rotate from the dashboard and update agent_api_key. The 401 response is timing-safe - same response for wrong vs missing. |
| No events showing in the dashboard | enable_agent not True, agent_endpoint wrong, or agent_project_id belongs to a different project than the API key | Verify all three. Trigger a request and wait one agent_flush_interval. |
| Decryption errors on the core side | Agent key doesn't match the project's wrapped key, key rotation happened but agents weren't updated, or encryption was disabled after the agent was configured | Pull the current key from the dashboard, or rotate again and update both sides together. |