# StellaOps Authority configuration template. # Copy to ../etc/authority.yaml (relative to the Authority content root) # and adjust values to fit your environment. Environment variables # prefixed with STELLAOPS_AUTHORITY_ override these values at runtime. # Example: STELLAOPS_AUTHORITY__ISSUER=https://authority.example.com schemaVersion: 1 # Absolute issuer URI advertised to clients. Use HTTPS for anything # beyond loopback development. issuer: "https://authority.stella-ops.local" # Token lifetimes expressed as HH:MM:SS or DD.HH:MM:SS. accessTokenLifetime: "00:02:00" refreshTokenLifetime: "30.00:00:00" identityTokenLifetime: "00:05:00" authorizationCodeLifetime: "00:05:00" deviceCodeLifetime: "00:15:00" # MongoDB storage connection details. storage: connectionString: "mongodb://localhost:27017/stellaops-authority" # databaseName: "stellaops_authority" commandTimeout: "00:00:30" # Signing configuration for revocation bundles and JWKS. signing: enabled: true activeKeyId: "authority-signing-2025-dev" keyPath: "../certificates/authority-signing-2025-dev.pem" algorithm: "ES256" keySource: "file" jwksCacheLifetime: "00:05:00" # provider: "default" additionalKeys: - keyId: "authority-signing-dev" path: "../certificates/authority-signing-dev.pem" source: "file" # Rotation flow: # 1. Generate a new PEM under ./certificates (e.g. authority-signing-2026-dev.pem). # 2. Trigger the .gitea/workflows/authority-key-rotation.yml workflow (or run # ops/authority/key-rotation.sh) with the new keyId/keyPath. # 3. Update activeKeyId/keyPath above and move the previous key into additionalKeys # so restarts retain retired material for JWKS consumers. notifications: ackTokens: enabled: true payloadType: "application/vnd.stellaops.notify-ack-token+json" defaultLifetime: "00:15:00" maxLifetime: "00:30:00" algorithm: "ES256" keySource: "file" activeKeyId: "notify-ack-2025-dev" keyPath: "../certificates/notify-ack-2025-dev.pem" keyUse: "notify-ack" jwksCacheLifetime: "00:05:00" additionalKeys: [] webhooks: enabled: true allowedHosts: - "hooks.slack.com" - "*.pagerduty.com" escalation: scope: "notify.escalate" requireAdminScope: true apiLifecycle: legacyAuth: enabled: true deprecationDate: "2025-11-01T00:00:00Z" sunsetDate: "2026-05-01T00:00:00Z" documentationUrl: "https://docs.stella-ops.org/migrations/authority/legacy-auth-endpoints" notificationTopic: "authority.api.deprecation" advisoryAi: remoteInference: enabled: false requireTenantConsent: true allowedProfiles: - "cloud-openai" - "sovereign-local" # Bootstrap administrative endpoints (initial provisioning). bootstrap: enabled: false apiKey: "change-me" defaultIdentityProvider: "standard" # Directories scanned for Authority plug-ins. Relative paths resolve # against the application content root, enabling air-gapped deployments # that package plug-ins alongside binaries. pluginDirectories: - "../StellaOps.Authority.PluginBinaries" # "/var/lib/stellaops/authority/plugins" # Plug-in manifests live in descriptors below; per-plugin settings are stored # in the configurationDirectory (YAML files). Authority will load any enabled # plugins and surface their metadata/capabilities to the host. plugins: configurationDirectory: "../etc/authority.plugins" descriptors: standard: type: "standard" assemblyName: "StellaOps.Authority.Plugin.Standard" enabled: true configFile: "standard.yaml" capabilities: - password - bootstrap - clientProvisioning metadata: defaultRole: "operators" # Example for an external identity provider plugin. Leave disabled unless # the plug-in package exists under StellaOps.Authority.PluginBinaries. ldap: type: "ldap" assemblyName: "StellaOps.Authority.Plugin.Ldap" enabled: false configFile: "ldap.yaml" capabilities: - password - mfa # OAuth client registrations issued by Authority. These examples cover Notify WebService # in dev (notify.dev audience) and production (notify audience). Replace the secret files # with paths to your sealed credentials before enabling bootstrap mode. clients: - clientId: "notify-web-dev" displayName: "Notify WebService (dev)" grantTypes: [ "client_credentials" ] audiences: [ "notify.dev" ] scopes: [ "notify.read", "notify.admin" ] senderConstraint: "dpop" auth: type: "client_secret" secretFile: "../secrets/notify-web-dev.secret" - clientId: "notify-web" displayName: "Notify WebService" grantTypes: [ "client_credentials" ] audiences: [ "notify" ] scopes: [ "notify.read", "notify.admin" ] senderConstraint: "dpop" auth: type: "client_secret" secretFile: "../secrets/notify-web.secret" - clientId: "concelier-ingest" displayName: "Concelier Ingestion" grantTypes: [ "client_credentials" ] audiences: [ "api://concelier" ] scopes: [ "advisory:ingest", "advisory:read" ] tenant: "tenant-default" senderConstraint: "dpop" auth: type: "client_secret" secretFile: "../secrets/concelier-ingest.secret" - clientId: "excitor-ingest" displayName: "Excititor VEX Ingestion" grantTypes: [ "client_credentials" ] audiences: [ "api://excitor" ] scopes: [ "vex:ingest", "vex:read" ] tenant: "tenant-default" senderConstraint: "dpop" auth: type: "client_secret" secretFile: "../secrets/excitor-ingest.secret" - clientId: "aoc-verifier" displayName: "AOC Verification Agent" grantTypes: [ "client_credentials" ] audiences: [ "api://concelier", "api://excitor" ] scopes: [ "aoc:verify", "advisory:read", "vex:read" ] tenant: "tenant-default" senderConstraint: "dpop" auth: type: "client_secret" secretFile: "../secrets/aoc-verifier.secret" - clientId: "airgap-operator" displayName: "AirGap Operations CLI" grantTypes: [ "client_credentials" ] audiences: [ "api://airgap-controller", "api://airgap-importer" ] scopes: [ "airgap:status:read", "airgap:import", "airgap:seal" ] tenant: "tenant-default" senderConstraint: "dpop" auth: type: "client_secret" secretFile: "../secrets/airgap-operator.secret" - clientId: "policy-engine" displayName: "Policy Engine Service" grantTypes: [ "client_credentials" ] audiences: [ "api://policy-engine" ] scopes: [ "policy:run", "findings:read", "effective:write" ] tenant: "tenant-default" properties: serviceIdentity: "policy-engine" senderConstraint: "dpop" auth: type: "client_secret" secretFile: "../secrets/policy-engine.secret" - clientId: "policy-cli" displayName: "Policy Automation CLI" grantTypes: [ "client_credentials" ] audiences: [ "api://policy-engine" ] scopes: [ "policy:read", "policy:author", "policy:review", "policy:simulate", "findings:read" ] tenant: "tenant-default" senderConstraint: "dpop" auth: type: "client_secret" secretFile: "../secrets/policy-cli.secret" - clientId: "exceptions-service" displayName: "Policy Engine Exceptions Worker" grantTypes: [ "client_credentials" ] audiences: [ "api://policy-engine" ] scopes: [ "exceptions:read", "exceptions:write" ] tenant: "tenant-default" senderConstraint: "dpop" auth: type: "client_secret" secretFile: "../secrets/exceptions-service.secret" - clientId: "console-web" displayName: "StellaOps Console" grantTypes: [ "authorization_code", "refresh_token" ] audiences: [ "console" ] scopes: [ "openid", "profile", "email", "ui.read", "authority:tenants.read", "advisory:read", "vex:read", "exceptions:read", "exceptions:approve", "aoc:verify", "findings:read", "airgap:status:read", "obs:read", "obs:incident", "timeline:read", "evidence:read", "attest:read", "orch:read", "vuln:read" ] # exceptions:approve is elevated via fresh-auth and requires an MFA-capable identity provider. tenant: "tenant-default" senderConstraint: "dpop" redirectUris: - "https://console.stella-ops.local/oidc/callback" postLogoutRedirectUris: - "https://console.stella-ops.local/" # Gateway must forward X-Stella-Tenant for /console endpoints; fresh-auth window (300s) # returned by /console/profile governs admin actions in the Console UI. auth: type: "client_secret" secretFile: "../secrets/console-web.secret" - clientId: "cartographer-service" displayName: "Cartographer Service" grantTypes: [ "client_credentials" ] audiences: [ "api://cartographer" ] scopes: [ "graph:write", "graph:read" ] tenant: "tenant-default" properties: serviceIdentity: "cartographer" senderConstraint: "dpop" auth: type: "client_secret" secretFile: "../secrets/cartographer-service.secret" - clientId: "packs-registry" displayName: "Packs Registry Service" grantTypes: [ "client_credentials" ] audiences: [ "api://packs-registry" ] scopes: [ "packs.read", "packs.write" ] tenant: "tenant-default" senderConstraint: "dpop" auth: type: "client_secret" secretFile: "../secrets/packs-registry.secret" - clientId: "task-runner" displayName: "Task Runner Service" grantTypes: [ "client_credentials" ] audiences: [ "api://task-runner" ] scopes: [ "packs.run", "packs.read" ] tenant: "tenant-default" senderConstraint: "dpop" auth: type: "client_secret" secretFile: "../secrets/task-runner.secret" - clientId: "pack-approver" displayName: "Pack Approver Automation" grantTypes: [ "client_credentials" ] audiences: [ "api://task-runner" ] scopes: [ "packs.approve", "packs.read" ] tenant: "tenant-default" senderConstraint: "dpop" auth: type: "client_secret" secretFile: "../secrets/pack-approver.secret" - clientId: "graph-api" displayName: "Graph API Gateway" grantTypes: [ "client_credentials" ] audiences: [ "api://graph-api" ] scopes: [ "graph:read", "graph:export", "graph:simulate" ] tenant: "tenant-default" senderConstraint: "dpop" auth: type: "client_secret" secretFile: "../secrets/graph-api.secret" - clientId: "export-center-operator" displayName: "Export Center Operator" grantTypes: [ "client_credentials" ] audiences: [ "api://export-center" ] scopes: [ "export.viewer", "export.operator" ] tenant: "tenant-default" senderConstraint: "dpop" auth: type: "client_secret" secretFile: "../secrets/export-center-operator.secret" - clientId: "export-center-admin" displayName: "Export Center Admin" grantTypes: [ "client_credentials" ] audiences: [ "api://export-center" ] scopes: [ "export.viewer", "export.operator", "export.admin" ] tenant: "tenant-default" senderConstraint: "dpop" auth: type: "client_secret" secretFile: "../secrets/export-center-admin.secret" - clientId: "notify-service" displayName: "Notify WebService" grantTypes: [ "client_credentials" ] audiences: [ "api://notify" ] scopes: [ "notify.viewer", "notify.operator" ] tenant: "tenant-default" senderConstraint: "dpop" auth: type: "client_secret" secretFile: "../secrets/notify-service.secret" - clientId: "notify-admin" displayName: "Notify Admin Automation" grantTypes: [ "client_credentials" ] audiences: [ "api://notify" ] scopes: [ "notify.viewer", "notify.operator", "notify.admin" ] tenant: "tenant-default" senderConstraint: "dpop" auth: type: "client_secret" secretFile: "../secrets/notify-admin.secret" - clientId: "observability-web" displayName: "Observability Console Backend" grantTypes: [ "client_credentials" ] audiences: [ "api://observability" ] scopes: [ "obs:read", "timeline:read", "evidence:read", "attest:read" ] tenant: "tenant-default" senderConstraint: "dpop" auth: type: "client_secret" secretFile: "../secrets/observability-web.secret" - clientId: "timeline-indexer" displayName: "Timeline Indexer Worker" grantTypes: [ "client_credentials" ] audiences: [ "api://timeline" ] scopes: [ "timeline:write", "timeline:read", "obs:read" ] tenant: "tenant-default" senderConstraint: "dpop" auth: type: "client_secret" secretFile: "../secrets/timeline-indexer.secret" - clientId: "evidence-locker" displayName: "Evidence Locker Service" grantTypes: [ "client_credentials" ] audiences: [ "api://evidence" ] scopes: [ "evidence:create", "evidence:read", "evidence:hold" ] tenant: "tenant-default" senderConstraint: "dpop" auth: type: "client_secret" secretFile: "../secrets/evidence-locker.secret" - clientId: "incident-bridge" displayName: "Incident Bridge Automation" grantTypes: [ "client_credentials" ] audiences: [ "api://observability" ] scopes: [ "obs:incident", "obs:read", "timeline:read", "timeline:write", "evidence:create" ] tenant: "tenant-default" senderConstraint: "dpop" auth: type: "client_secret" secretFile: "../secrets/incident-bridge.secret" - clientId: "vuln-explorer-ui" displayName: "Vuln Explorer UI" grantTypes: [ "client_credentials" ] audiences: [ "api://vuln-explorer" ] scopes: [ "vuln:read" ] tenant: "tenant-default" senderConstraint: "dpop" auth: type: "client_secret" secretFile: "../secrets/vuln-explorer-ui.secret" # Signals sensors must request aoc:verify alongside write scope. - clientId: "signals-uploader" displayName: "Signals Sensor" grantTypes: [ "client_credentials" ] audiences: [ "api://signals" ] scopes: [ "signals:write", "signals:read", "aoc:verify" ] tenant: "tenant-default" senderConstraint: "dpop" auth: type: "client_secret" secretFile: "../secrets/signals-uploader.secret" tenants: - name: "tenant-default" roles: orch-viewer: scopes: [ "orch:read" ] orch-operator: scopes: [ "orch:read", "orch:operate" ] orch-admin: scopes: [ "orch:read", "orch:operate", "orch:quota", "orch:backfill" ] policy-author: scopes: [ "policy:author", "policy:read", "policy:simulate", "findings:read" ] policy-reviewer: scopes: [ "policy:review", "policy:read", "policy:simulate", "findings:read" ] policy-approver: scopes: [ "policy:approve", "policy:review", "policy:read", "policy:simulate", "findings:read" ] policy-operator: scopes: [ "policy:operate", "policy:run", "policy:activate", "policy:read", "policy:simulate", "findings:read" ] policy-auditor: scopes: [ "policy:audit", "policy:read", "policy:simulate", "findings:read" ] pack-viewer: scopes: [ "packs.read" ] pack-operator: scopes: [ "packs.read", "packs.run" ] pack-publisher: scopes: [ "packs.read", "packs.write" ] pack-approver: scopes: [ "packs.read", "packs.approve" ] pack-admin: scopes: [ "packs.read", "packs.write", "packs.run", "packs.approve" ] export-viewer: scopes: [ "export.viewer" ] export-operator: scopes: [ "export.viewer", "export.operator" ] export-admin: scopes: [ "export.viewer", "export.operator", "export.admin" ] notify-viewer: scopes: [ "notify.viewer" ] notify-operator: scopes: [ "notify.viewer", "notify.operator" ] notify-admin: scopes: [ "notify.viewer", "notify.operator", "notify.admin" ] observability-viewer: scopes: [ "obs:read", "timeline:read", "evidence:read", "attest:read" ] observability-investigator: scopes: [ "obs:read", "timeline:read", "timeline:write", "evidence:read", "evidence:create", "attest:read" ] observability-legal: scopes: [ "evidence:read", "evidence:hold" ] observability-incident-commander: scopes: [ "obs:read", "obs:incident", "timeline:read", "timeline:write", "evidence:create", "evidence:read", "attest:read" ] airgap-viewer: scopes: [ "airgap:status:read" ] airgap-operator: scopes: [ "airgap:status:read", "airgap:import" ] airgap-admin: scopes: [ "airgap:status:read", "airgap:import", "airgap:seal" ] advisory-ai-viewer: scopes: [ "advisory-ai:view" ] advisory-ai-operator: scopes: [ "advisory-ai:view", "advisory-ai:operate" ] advisory-ai-admin: scopes: [ "advisory-ai:view", "advisory-ai:operate", "advisory-ai:admin" ] advisoryAi: remoteInference: consentGranted: false consentVersion: "" consentedAt: "" consentedBy: "" # Exception approval routing templates used by Policy Engine and Console. exceptions: routingTemplates: - id: "secops" authorityRouteId: "approvals/secops" requireMfa: true description: "Security Operations approval chain" - id: "governance" authorityRouteId: "approvals/governance" requireMfa: false description: "Governance review (non-production)" # CIDR ranges that bypass network-sensitive policies (e.g. on-host cron jobs). # Keep the list tight: localhost is sufficient for most air-gapped installs. bypassNetworks: - "127.0.0.1/32" - "::1/128" # Security posture (rate limiting + sender constraints). security: rateLimiting: token: enabled: true permitLimit: 30 window: "00:01:00" queueLimit: 0 authorize: enabled: true permitLimit: 60 window: "00:01:00" queueLimit: 10 internal: enabled: false permitLimit: 5 window: "00:01:00" queueLimit: 0 senderConstraints: dpop: enabled: true allowedAlgorithms: [ "ES256", "ES384" ] proofLifetime: "00:02:00" allowedClockSkew: "00:00:30" replayWindow: "00:05:00" nonce: enabled: true ttl: "00:10:00" maxIssuancePerMinute: 120 store: "memory" # Set to "redis" for multi-node Authority deployments. requiredAudiences: - "signer" - "attestor" # redisConnectionString: "redis://authority-redis:6379?ssl=false" mtls: enabled: false requireChainValidation: true rotationGrace: "00:15:00" enforceForAudiences: - "signer" # Requests for these audiences force mTLS sender constraints allowedSanTypes: - "dns" - "uri" allowedCertificateAuthorities: [ ] allowedSubjectPatterns: [ ] advisoryAi: remoteInference: enabled: false requireTenantConsent: true allowedProfiles: []