Configuration

UrNammu uses a combination of environment variables and runtime settings stored in the database. Runtime settings can be changed from the Settings and Integrations pages without redeployment, and anything secret is encrypted at rest with SETTINGS_ENCRYPTION_KEY.

Environment variables

Set these in your .env file or hosting provider's environment config. The full list, with comments, lives in .env.example.

Required

# Database connection
DATABASE_URL=postgresql://user:pass@host:5432/urnammu

# NextAuth session encryption
NEXTAUTH_SECRET=generate-a-random-32-char-string
NEXTAUTH_URL=https://your-domain.com

# Encrypts admin API keys and service credentials stored in Settings
SETTINGS_ENCRYPTION_KEY=generate-with-openssl-rand-base64-32

Authentication

# Google OAuth
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret

# Microsoft 365 / Entra ID sign-in
MICROSOFT_CLIENT_ID=
MICROSOFT_CLIENT_SECRET=
MICROSOFT_TENANT_ID=

# Password-backed local accounts
ENABLE_LOCAL_AUTH=true

# Dev login (development only!)
ENABLE_DEV_LOGIN=true   # Set to "false" in production

Optional

# AI provider fallbacks (if not configured in Settings UI)
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...

# API proxy shared secret
PROXY_SECRET=your-shared-secret

# Salt for the prompt correlation hash. Also settable as the prompt_hash_salt
# setting; falls back to NEXTAUTH_SECRET. Set the SAME value on the Azure proxy
# or its hashes will not line up with the app's.
PROMPT_HASH_SALT=your-hash-salt

# Authenticates every /api/cron/* route
CRON_SECRET=your-cron-secret

# Endpoint agent enrollment secret (Settings > Endpoint Agent wins).
# Low-value by design: it can enroll a device and nothing else.
ENDPOINT_AGENT_ENROLLMENT_SECRET=

# Shadow AI blocklist feed token (the denylist your DNS / proxy / CASB polls)
SHADOW_AI_BLOCKLIST_TOKEN=your-feed-token

# Google Workspace / Microsoft 365 shadow AI scanning fallbacks
GOOGLE_SERVICE_ACCOUNT_KEY={"type":"service_account",...}
GOOGLE_ADMIN_EMAIL=admin@your-domain.com
MICROSOFT_SHADOW_AI_TENANT_ID=
MICROSOFT_SHADOW_AI_CLIENT_ID=
MICROSOFT_SHADOW_AI_CLIENT_SECRET=

# Directory sync (both default to off)
DIRECTORY_SYNC_GOOGLE_WORKSPACE_ENABLED=false
DIRECTORY_SYNC_MICROSOFT_365_ENABLED=false

# Claude Code / Cursor OpenTelemetry ingestion (developer AI dashboards)
CLAUDE_CODE_TELEMETRY_SECRET=
CURSOR_TELEMETRY_SECRET=
TELEMETRY_MAX_ROWS=5000   # Rows per ingest request; larger bodies get a 413

# Gemini / Vertex AI billing export, AI gateways, Hexnode, Datadog, and
# Resend (scheduled report email) all have env fallbacks too — see .env.example

Telemetry sources

Each assistant telemetry source is its own provider sync with its own credentials. All of these are normally configured in the app rather than as environment variables.

# GitHub Copilot usage metrics
GITHUB_COPILOT_TOKEN=
GITHUB_COPILOT_ORG=
GITHUB_COPILOT_ENTERPRISE=

# ChatGPT Enterprise Compliance API (workspace-scoped Admin key)
CHATGPT_ENTERPRISE_ADMIN_KEY=
CHATGPT_WORKSPACE_ID=

# Anthropic Compliance API activity feed. A Compliance Access Key with
# read:compliance_activities, plus read:compliance_user_data for Claude app
# session metadata. The Admin API key serves the activity feed on its own.
ANTHROPIC_COMPLIANCE_KEY=
ANTHROPIC_COMPLIANCE_LOOKBACK_DAYS=30
ORG_TIMEZONE=UTC

# Claude Enterprise Analytics API (read:analytics, Enterprise plans only)
ANTHROPIC_ANALYTICS_KEY=

Retention

Days retained per collection table, enforced by the daily prune crons. Set these in Settings → General → Data Retention; the env vars are the fallback. 0 disables pruning for that table. Usage and cost buckets are the long-term aggregate and are never pruned.

RAW_SNAPSHOT_RETENTION_DAYS=14           # ProviderRawSnapshot
API_USAGE_LOG_RETENTION_DAYS=180         # APIUsageLog (proxy request log)
AGENT_TOOL_CALL_RETENTION_DAYS=180       # AgentToolCall
POLICY_DENIAL_RETENTION_DAYS=365         # PolicyDenial
PROXY_HEALTH_RETENTION_DAYS=90           # ProxyHealthSnapshot
SCAN_RESULT_RETENTION_DAYS=365           # SensitiveScan, ProviderSecurityScan
COMPLIANCE_ACTIVITY_RETENTION_DAYS=365   # ComplianceActivity
CLAUDE_CODE_TELEMETRY_RETENTION_DAYS=30
CURSOR_TELEMETRY_RETENTION_DAYS=30

Runtime settings

These are managed from the Settings and Integrations pages in the app and stored in the AppSetting table. They take precedence over environment variables, and the API masks secret values when reading them back.

Settings > General

  • AI Provider — Anthropic Claude or OpenAI, used for risk classification, compliance gap analysis, agent risk review, and shadow AI auto-fill
  • AI Model — The specific model to call
  • API Key — Provider-specific API key
  • Policy enforcement mode — off, dry-run, or enforce for machine-readable policy rules evaluated at the proxy
  • Data retention — Retention window in days per collection table, enforced by the daily prune crons

Settings > Provider Admin APIs

  • Anthropic and OpenAI admin keys — Organization-level usage and cost sync
  • Gemini / Vertex AI billing export — BigQuery billing dataset for Google usage
  • Per-provider schedules — A global enable flag and interval, plus provider_sync_<provider>_enabled and provider_sync_<provider>_interval_hours overrides for any single provider. Each provider's due-ness is tracked from its own last successful run, so one stalled sync cannot hold up the rest
  • Sync History & Backfill — Each provider's "history from" and watermark dates with its maximum lookback, plus a backfill control that walks a UTC date range in 7-day chunks with a per-chunk log and a cancel button
  • Usage Attribution — A default governed system per provider and per-API-key overrides, applied to both usage and cost rows
  • Anomaly detection — Recent vs baseline windows, minimum thresholds, and per-dimension sensitivity

Providers that only report through their own compliance or analytics endpoints — GitHub Copilot, ChatGPT Enterprise, the Anthropic Compliance API, and the Claude Enterprise Analytics API — are configured on the Integrations hub and run on the same per-provider schedule.

Settings > Proxy Setup

  • Proxy URL and secret — Azure Functions proxy endpoint and shared authentication secret
  • Per-provider base URLs — anthropic, openai/v1, azure-openai, gemini, and bedrock, with ready-to-paste TypeScript, Python, and cURL snippets, plus Claude Code managed and per-user settings
  • Azure OpenAI resource and deployment map — azure_openai_endpoint and azure_openai_deployments, which maps deployment names to model ids so Azure traffic prices correctly
  • Attribution headers — x-ai-system-id and x-agent-id, plus x-user-email and x-department. x-agent-id is what applies an agent’s governance on the call: MCP allowlists, the kill switch, and human-review triggers. Clients must handle 403 responses of type agent_blocked, policy_denied and human_review_required (the last carries a pending review id)
  • Prompt hash salt — prompt_hash_salt, read by both the app and the proxy so dangerous-prompt alerts fold by prompt across the proxy, Claude Code, and Cursor

Settings > Users & Identity

  • Directory sync — One card per identity provider with an auto-sync toggle (default off), interval (default 24 hours), last run counts, and a manual run. Reuses the Shadow AI credentials; Google additionally needs the admin.directory.user.readonly delegation scope and Microsoft needs User.Read.All
  • Google Sign-In — Configure Google OAuth client ID and secret
  • Microsoft 365 — Configure Entra ID OAuth credentials
  • Local Auth — Enable/disable local email+password authentication
  • Users — Assign roles, suspend, or delete accounts
  • Dev Login — Enable/disable development login (never in production)

Settings > Shadow AI

  • Google Workspace and Microsoft 365 discovery — Service credentials, scan lookback, and schedule
  • Hexnode UEM, CrowdStrike Falcon, Netskope — Device, endpoint, and log-shipper sources
  • Blocklist feed token — Generates the bearer token for the enforcement feed; the feed fails closed with no token set
  • Enforcement readiness — Shows whether the network and identity enforcement layers are actually wired up

Settings > Reporting

  • Email delivery — Resend credentials and sender for scheduled report delivery

Integrations hub

Integrations groups every external connection in one place: AI model providers, provider telemetry, AI gateways (Helicone, OpenRouter, Portkey, LiteLLM), assistant telemetry (GitHub Copilot usage metrics, the ChatGPT Enterprise Compliance API, the Anthropic Compliance API activity feed, and the Claude Enterprise Analytics API), MDM and endpoint sources, and observability sinks (Azure Monitor, Datadog). Configuring a gateway here also makes it a Sensitive Scan probe target and a Provider Security scan subject.

Roles and permissions

UrNammu has three user roles:

  • ADMIN — Full access. Can manage settings, users, and all entities. The first user to sign in is automatically ADMIN.
  • COMPLIANCE_OFFICER — Can manage policies, risk assessments, and compliance data. Cannot access system settings or user management.
  • VIEWER — Read-only access to all dashboards and data. Default role for new users.

Directory sync

A daily sync pulls people from Google Workspace (admin.directory.user.readonly, which must be added to the existing domain-wide delegation) or Microsoft Graph (User.Read.All) into DirectoryPerson. Both sources default to off and are controlled by directory_sync_<source>_enabled and directory_sync_<source>_interval_hours.

With a directory connected, every observed email is resolved through the identity provider's alias map first, so one person seen under two addresses is one row keyed by their primary address, and names and departments on Usage by Person come from the directory rather than guesswork. Shadow AI users roll up by department, UrNammu accounts whose directory record was disabled are suspended automatically, and continued usage by an offboarded person raises a usage_after_deactivation alert.

Cost attribution

Usage and cost rows carry the same attribution columns: the API key that incurred the spend where the provider reports it, the provider-side workspace, and the governed system resolved through Settings → Provider Admin APIs → Usage Attribution. Oversight surfaces this as Cost by Governed System, with the attributed share and an unattributed remainder, and Cost by API Key. Anthropic's cost report cannot be grouped by API key, so its rows are workspaces; attribution takes effect on the next sync and historical rows are re-attributed as the rolling window re-pulls them.

Shadow AI discovery and enforcement

The known-tools registry

Every detection method matches observations against a built-in registry of 164 known AI tools across 16 categories — chat assistants, coding assistants, agent platforms, image, video and audio generation, writing, meeting notes, search, ML platforms, data analysis, productivity, translation, customer support, browser extensions, and other. Matching weighs domain, exact and fuzzy name, publisher, OAuth scope, and app or bundle id, and the more specific match wins on a tie. A discovery inherits the category of the entry it matched; anything the registry does not know shows as uncategorized and can be set by hand during triage. Entries also carry informational risk hints — trains_on_data, consumer_grade, china_hosted, no_enterprise_tier — as reviewer context.

Rows created before categories existed are filled in the next time a scan observes the tool, or all at once with npx tsx scripts/backfill-tool-categories.ts (--dry-run reports what would change).

Identity sources

Google Workspace and Microsoft 365 scans read the OAuth tokens users have granted to third-party apps and match them against the registry. Each discovery records the distinct user emails seen, the OAuth scopes granted, and first and last seen timestamps, so a tool's reach is queryable rather than buried in notes. Where a tool is federated through the IdP, the scan also captures the app handle used later for enforcement.

Device and endpoint sources

Hexnode UEM reports installed applications on managed devices; CrowdStrike Falcon contributes endpoint signals. Both feed the same deduplicated triage queue.

DNS / proxy / Netskope log import

Import CSV or TXT files containing DNS queries or proxy logs, ship Netskope logs, or post JSON to the ingestion API. Hostnames are normalized before matching, and each candidate carries a confidence score. Tools already governed in the registry are suppressed automatically.

Manual reporting

Employees can report AI tools they discover via the Shadow AI page. These are added to the triage queue for review by governance teams.

Enforcing a block

UrNammu is not in your network path, so a Blocked status only stops a tool when one of two enforcement layers is connected. The blocklist feed at /api/discovered-tools/blocklist publishes blocked domains as bare text, a hosts file, JSON, or a PAC file for a DNS sinkhole, proxy ACL, firewall, or CASB to poll. The identity layer disables the app at Google Workspace or Microsoft Entra ID so sign-ins stop. Settings > Shadow AI reports the readiness of both.