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, orenforcefor 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>_enabledandprovider_sync_<provider>_interval_hoursoverrides 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, andbedrock, 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_endpointandazure_openai_deployments, which maps deployment names to model ids so Azure traffic prices correctly - Attribution headers —
x-ai-system-idandx-agent-id, plusx-user-emailandx-department.x-agent-idis what applies an agent’s governance on the call: MCP allowlists, the kill switch, and human-review triggers. Clients must handle403responses of typeagent_blocked,policy_deniedandhuman_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.readonlydelegation scope and Microsoft needsUser.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.