Skip to main content

Integration Patterns

Arcan supports four integration patterns for external services.

Pattern A: REST API (Primary)

Every external service integrates via the REST API with an API token:

# Create an API token
arcan token create --name "ci-pipeline" --ttl 90d --capabilities "secrets:read,leases:create"

# Use it
curl -H "Authorization: Bearer arc_xxx" https://arcan.example.com/api/v1/realms/myapp/secrets

Token Structure

  • Prefix: arc_ (API tokens), oas_ (OIDC/K8s sessions)
  • Token stored as SHA-256 hash in database (not plaintext)
  • Each token has a name, TTL, and capability list
  • Capabilities are a subset of the issuing user's RBAC permissions
  • Tokens can be revoked individually or all-at-once

Rate Limiting

  • Per-token: 100 req/s (configurable per token)
  • Per-IP (unauthenticated): 10 req/s
  • Rate limit headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset

Pattern B: Client SDKs (Convenience)

Thin wrappers around the REST API. Official SDKs:

Phase 1: Go SDK      (dogfood -- we use it)
Phase 2: TypeScript (largest community)
Phase 3: Python (data/ML pipelines)

SDKs are generated from an OpenAPI spec maintained alongside the API source code.

Go SDK

client := arcan.NewClient("https://arcan.example.com", arcan.WithToken("arc_xxx"))
secret, err := client.KV.Get(ctx, "DATABASE_URL", arcan.InRealm("myapp"))
creds, err := client.Lease.Create(ctx, "postgres", "readonly", arcan.WithTTL(1*time.Hour))
defer client.Lease.Revoke(ctx, creds.LeaseID)

TypeScript SDK

const client = new ArcanClient({ url: 'https://arcan.example.com', token: 'arc_xxx' });
const secret = await client.kv.get('DATABASE_URL', { realm: 'myapp' });
const creds = await client.lease.create('postgres', 'readonly', { ttl: '1h' });

Python SDK

client = ArcanClient(url="https://arcan.example.com", token="arc_xxx")
secret = client.kv.get("DATABASE_URL", realm="myapp")
creds = client.lease.create("postgres", "readonly", ttl="1h")

Pattern C: Webhooks (Event-Driven)

Arcan pushes events to external services via the audit webhook sink:

{
"event": "lease.expiring",
"timestamp": "2026-04-01T12:00:00Z",
"request_id": "req_xxx",
"realm": "myapp",
"data": {
"lease_id": "uuid",
"engine": "postgres",
"role": "readonly",
"expires_at": "2026-04-01T13:00:00Z",
"ttl_remaining_seconds": 3600
}
}

Event Categories

  • secret.* -- Secret changes (set, deleted, rotated)
  • lease.* -- Lease lifecycle (created, renewed, expiring, expired, revoked)
  • policy.* -- Policy changes (role created, binding changed)
  • engine.* -- Engine lifecycle (bootstrapped, root rotated, health changed)
  • auth.* -- Auth events (login, token created, token revoked)
  • plugin.* -- Plugin lifecycle (installed, upgraded, crashed, removed)

Delivery Rules

  • Retry: 3 attempts with exponential backoff (1s, 5s, 30s)
  • Timeout: 10s per attempt
  • Payload signature: HMAC-SHA256 in X-Arcan-Signature header
  • Dead letter: failed events logged to audit table with delivery error

Pattern D: Agent Mode (Future)

arcan agent --realm myapp --inject DATABASE_URL,API_KEY --refresh 5m

Runs as a sidecar/daemon:

  • Fetches secrets on startup, writes to tmpfs file or environment
  • Auto-refreshes before TTL expiry
  • Auto-renews dynamic credential leases
  • Emits health status for container orchestrator probes

The core architecture must not preclude this. The API and token system already support it. The agent is a consumer of the REST API, not a new integration pattern.