Features
Access Control, Fleet Visibility, and Health — in One Place
Aerie is the Renkara fleet launcher and permission dashboard. Authenticated users land on a curated tile grid showing exactly the tools they have been granted access to — nothing more, nothing less. Admins get a spreadsheet-style grant matrix for toggling per-user, per-tool permissions with one click. User records are minted on first login from the JWT (no manual provisioning). Every tile carries a live health indicator sourced from Vigil, the fleet’s monitoring service, in a single cached call. FastMCP exposes the entire permission surface to Claude Code — list tools, grant and revoke access, manage admins, query health — all without opening a browser. FastAPI backend, React 19 frontend, PostgreSQL 17, renkara-auth OIDC, structlog, OpenTelemetry, and Prometheus /metrics.
Auth & Identity
OIDC In, Nobody Sneaks Through
renkara-auth OIDC Gate
Every request — page load, tile click, API call — is validated against the
renkara-authissuer (https://auth.renkara.com, realmrenkara-auth). RS256 JWTs are verified against a JWKS cache refreshed every 24 hours. Expired or unsigned tokens are rejected before a single database row is touched.Five-Layer Auth Chain
Auth resolves in priority order: dev stub (
AUTH_DISABLED=true), mTLS client cert viaavian_diagnostics, SHA-256 service token, plain API key, and finally RS256 JWT. Each layer falls through cleanly to the next. Service-to-service calls never hit the OIDC stack.Admin Elevation
A request is treated as admin if the JWT role is in
{'admin','owner','superadmin'}, theis_adminclaim is set, or the mirroreddashboard_userrow carriesis_dashboard_admin=true. Admin emails can be pre-seeded via theADMIN_EMAILSenv var so the first operator gets access before anyone has logged in.JWKS Cache
Public keys are fetched once from
AUTH_PUBLIC_KEY_URLand cached in memory for 24 hours. Cold-start validation never blocks; the cache is populated asynchronously during the FastAPI lifespan startup event. CORS allows*.renkara.com,*.accelastudy.ai, and everylocalhost:*port without per-tool configuration.
Permission Model
Default None — Every Grant Is Explicit
Soft Launcher Gate
Aerie controls which tool tiles a user sees and can click. It does not enforce access at the target tool — each tool still admits any valid renkara-auth session. The security promise is UX hygiene and workflow friction, not an iron perimeter. Target tools remain responsible for their own access control.
Default None
A user with no grants sees an empty launcher. The access level
noneis represented by the absence of a row intool_grants—'none'is never stored. This means the grant matrix stays clean and there is no ambiguity between “revoked” and “never granted.”Per-User, Per-Tool Grants
Each
tool_grantsrow is keyed on(user_id, tool_key)with a composite primary key.access_levelis currently'view'(tile visible and launchable) or absent (no tile). A future'admin'level is reserved in the schema for per-tool admin delegation without requiring a model migration.Audit Trail
Every grant row records
granted_by(the admin UUID who created or modified it) andgranted_at(timestamp). Revoking a grant deletes the row, preserving the history in structlog and OpenTelemetry spans rather than soft-deleting it into an ever-growing audit table.
Admin Matrix
The Whole Fleet on One Screen
Grant Matrix View
Admins reach
/adminand see every known user as a row and every registered tool as a column. Each cell is a toggle. One click grantsviewaccess; a second click revokes it. The matrix re-fetches state after every mutation so it never goes stale between tabs.Bulk Grant & Revoke
The admin API exposes bulk upsert endpoints (
POST /api/v1/admin/grants/bulk) that accept a list of(user_id, tool_key, access_level)tuples and apply them in a single database transaction. Useful for onboarding a new team member across a dozen tools, or rolling back a batch of grants after an offboarding.User Management
Admins can list all dashboard users, toggle
is_dashboard_adminon any user, and inspect first- and last-seen timestamps. No manual user creation required — JIT mirroring handles provisioning on first login. Deactivating a user is done by revoking all their grants; the mirrored row is retained for audit purposes.Tool Registry Management
Admins can activate and deactivate tool tiles, update descriptions, URLs, API URLs, icons, accent colors, categories, health paths, and sort order from the admin panel. The registry is also seed-loadable via
seed_tools.pyfor CI bootstrapping and disaster recovery.
JIT User Mirror
First Login Provisions Everything
JIT Upsert on
/api/v1/meThe first thing any authenticated frontend call does is hit
GET /api/v1/me. If adashboard_usersrow for the JWT subject doesn’t exist, it is created atomically with the email, display name, and admin flag from the token. If it does exist,last_seen_atis bumped and the admin flag is reconciled. Zero manual provisioning, zero delayed access.Identity Sync
Name and email are re-synced from the JWT on every
/mecall. If a user’s display name changes in renkara-auth, Aerie reflects it on their next login without any admin intervention. Theidcolumn is a stable UUID derived from the auth subject, not from auto-increment.Admin Flag Resolution
is_dashboard_adminis set totrueon JIT upsert if the JWT email matches any entry in theADMIN_EMAILSCSV, or if the JWT carries a qualifying role oris_adminclaim. Once set, it persists across logins unless an admin explicitly revokes it via the matrix.
Tool Registry & Health
Every Tool, Its Status, Right Now
Tool Registry
The
toolstable is the canonical record of every tool in the fleet: key (slug), name, description, frontend URL, API URL, accent color, icon URL, category, auth realm, health path, sort order, and active status. Aerie ships with a 22-tool seed covering the full current fleet.Fleet Health from Vigil
Tile health comes from Vigil, which already monitors the fleet on a schedule with history and incidents. Aerie’s backend asks it once via
GET /api/v1/fleet/diagnosticsand caches the answer process-wide, so the launcher makes one request per minute regardless of how many tiles are on screen — not one per tile. Statuses normalize tohealthy,degraded,down, orunknown.Single Fleet-Status Endpoint
GET /api/v1/fleet-statusreturns{tool_key: status}for the whole fleet in one call. It never fails the page: when Vigil is unreachable it serves the last good snapshot flaggedstalerather than blanking every tile, and it holds no database connection while it waits.Category & Sort
Tools carry a
categoryfield and a numericsort_order. The launcher grid groups tiles by category and sorts within each group bysort_order. Admins can drag-to-reorder within the matrix; the backend persists the new order in a single bulk-update call.
MCP Server
Manage the Fleet from Claude Code
FastMCP Streamable HTTP
Aerie’s FastMCP server mounts at
/mcpon the same FastAPI process. Streamable HTTP means no separate sidecar process and no WebSocket upgrade — Claude Code connects to the samehttps://aerie-api.renkara.com/mcpURL used for everything else. Authentication flows through the same five-layer chain.stdio Bridge
A standalone
mcp-server/server.pybridges the FastMCP tools over stdio for Claude Code MCP registration (~/.claude/mcp_servers.json). The bridge delegates every call to the HTTP API, so tool implementations live in one place and the stdio path is a thin shim.MCP Tool Surface
MCP tools cover the full permission surface: list tools, get a tool, list users, get a user, list grants, grant access, revoke access, bulk-grant, promote or demote admins, and query tool health. Operators can manage the entire fleet access model from a Claude Code conversation without opening a browser.
Service-to-Service Auth
MCP calls from Claude Code authenticate via the
API_KEYbearer orSERVICE_TOKENSSHA-256 map — no user JWT required. This lets automation scripts and scheduled agents manage grants without holding a human session token. Token comparison useshmac.compare_digestthroughout.
Accessibility
Built for Everyone
WCAG 2.1 AA Compliance
4.5:1 contrast for body text, 3:1 for large text and UI components, in both light and dark themes.
Keyboard Navigation
Every interaction reachable via keyboard. Logical tab order, visible focus indicators, Escape-to-dismiss for modals.
Screen Reader Support
VoiceOver, NVDA, and JAWS tested. Semantic HTML, ARIA labels, live regions for dynamic updates.
Reduced Motion
Respects
prefers-reduced-motion. Usable at 200% zoom. Touch targets meet 44x44 minimum.
How It Works
Login, See Your Fleet, Stay in Control
Step 1: Authenticate
The user hits
aerie.renkara.comand is redirected through the renkara-auth OIDC flow. On callback, the RS256 JWT is verified, thedashboard_usersrow is JIT-upserted, and the session is established. First-time users are provisioned in milliseconds with no admin involvement.Step 2: Launch
The launcher queries
/api/v1/toolsand receives only the tools the authenticated user has been grantedviewaccess to. Each tile shows the tool name, description, accent color, icon, and a live health dot. Clicking a tile opens the target tool in a new tab.Step 3: Administer
Admins navigate to
/adminand see the full grant matrix — every user, every tool, every toggle. One click grants or revokes access. Bulk operations onboard entire teams in a single API call. User admin flags are toggled in the same view.Step 4: Automate
Claude Code connects to the MCP server and manages grants conversationally. A single prompt can grant a new team member access to a curated set of tools, revoke a departed employee’s entire access, or query the health of every tool in the fleet — no browser, no manual toggling.
Technical Specifications
Under the Hood
Backend
- FastAPI (Python 3.12)
- SQLAlchemy 2.0 async (
Mapped[]style) + asyncpg - PostgreSQL 17 — dedicated
aeriedatabase on shared RDS - FastMCP for the MCP tool surface (Streamable HTTP
/mcp+ stdio bridge) - Alembic async migrations (versioned,
0001_initial_schema.py) - Pydantic-Settings v2 (
BaseSettings) — env-var-first config - structlog + OpenTelemetry instrumentation + Prometheus
/metrics - chronicle-logdrain sidecar for prod log aggregation
Frontend
- React 19 + TypeScript ~5.6.2 + Vite 8
- @avian/design-system tokens, components, and Tailwind preset
- @avian/auth-react 0.1.4 (OIDC via
auth.renkara.com) - Light and dark mode (WCAG 2.1 AA, azure accent
hsl(212 90% 48%)) - Vitest component test suite with coverage
- Routes:
LoginPage,LauncherPage,AdminPage
Permission Model
- Default access:
none(absence of atool_grantsrow) - Granted access:
view(tile visible & launchable) - Reserved:
adminlevel (schema-ready, not yet enforced) - Composite PK:
(user_id UUID, tool_key str) - Audit fields:
granted_by UUID,granted_at timestamp - Soft launcher gate — does not replace target-tool auth
- Default access:
Auth Chain
- Layer 1:
AUTH_DISABLED=truestub (local dev) - Layer 2: mTLS via
avian_diagnostics.mtls - Layer 3: SHA-256 service token (
hmac.compare_digest) - Layer 4: Plain API key bearer
- Layer 5: RS256 JWT, JWKS cached 24h from
AUTH_PUBLIC_KEY_URL - Admin: JWT role in
{'admin','owner','superadmin'}—or—is_adminclaim —or—dashboard_user.is_dashboard_admin
- Layer 1:
Infra & Deploy
- Frontend: CloudFront + S3 (
aerie-renkara-frontend) athttps://aerie.renkara.com - Backend: ALB + ECS Fargate, ECR repo
aerie, athttps://aerie-api.renkara.com - Auth:
https://auth.renkara.com, OIDC clientaerie - Frontend port 3448 / backend port 3449 (local dev)
- Deploy by push to
main— CodePipeline handles Source → Build → Deploy - Entrypoint:
alembic upgrade headthenuvicorn
- Frontend: CloudFront + S3 (
Development
100% Built by Claude
Every tool in the Renkara fleet was built by Claude (Anthropic) working alongside a single human supervisor. Every line of code, every test, every deployment: AI-authored with human direction. The leverage factor across the fleet runs in the 20x–50x range, with individual sessions regularly exceeding 100x.
See the daily leverage records for per-task numbers across the full build history.