# Authority Plug-in Developer Guide > **Status:** Updated 2025-10-11 (AUTHPLUG-DOCS-01-001) with lifecycle + limiter diagrams and refreshed rate-limit guidance aligned to PLG6 acceptance criteria. ## 1. Overview Authority plug-ins extend the **StellaOps Authority** service with custom identity providers, credential stores, and client-management logic. Unlike Concelier plug-ins (which ingest or export advisories), Authority plug-ins participate directly in authentication flows: - **Use cases:** integrate corporate directories (LDAP/AD)[^ldap-rfc], delegate to external IDPs, enforce bespoke password/lockout policies, or add client provisioning automation. - **Constraints:** plug-ins load only during service start (no hot-reload), must function without outbound internet access, and must emit deterministic results for identical configuration input. - **Ship targets:** build against the host’s .NET 10 preview SDK, honour offline-first requirements, and surface actionable diagnostics so operators can triage issues from `/ready`. ## 2. Architecture Snapshot Authority hosts follow a deterministic plug-in lifecycle. The exported diagram (`docs/assets/authority/authority-plugin-lifecycle.svg`) mirrors the steps below; regenerate it from the Mermaid source if you update the flow. 1. **Configuration load** – `AuthorityPluginConfigurationLoader` resolves YAML manifests under `etc/authority.plugins/`. 2. **Assembly discovery** – the shared `PluginHost` scans `StellaOps.Authority.PluginBinaries` for `StellaOps.Authority.Plugin.*.dll` assemblies. 3. **Registrar execution** – each assembly is searched for `IAuthorityPluginRegistrar` implementations. Registrars bind options, register services, and optionally queue bootstrap tasks. 4. **Runtime** – the host resolves `IIdentityProviderPlugin` instances, uses capability metadata to decide which OAuth grants to expose, and invokes health checks for readiness endpoints. ![Authority plug-in lifecycle diagram](../assets/authority/authority-plugin-lifecycle.svg) _Source:_ `docs/assets/authority/authority-plugin-lifecycle.mmd` ### 2.1 Component boundaries The Standard plug-in ships with a small, opinionated surface: configuration is bound during registrar execution, capability metadata feeds the host, and credential/audit flows stay deterministic and offline-friendly. The component view below highlights those boundaries and where operators supply bundles (secrets, offline kits) for air-gapped installs. ![Standard plug-in component topology](../assets/authority/authority-plugin-component.svg) _Source:_ `docs/assets/authority/authority-plugin-component.mmd` **Data persistence primer:** the standard Mongo-backed plugin stores users in collections named `authority_users_` and lockout metadata in embedded documents. Additional plugins must document their storage layout and provide deterministic collection naming to honour the Offline Kit replication process. ## 3. Capability Metadata Capability flags let the host reason about what your plug-in supports: - Declare capabilities in your descriptor using the string constants from `AuthorityPluginCapabilities` (`password`, `mfa`, `clientProvisioning`, `bootstrap`). The configuration loader now validates these tokens and rejects unknown values at startup. - `AuthorityIdentityProviderCapabilities.FromCapabilities` projects those strings into strongly typed booleans (`SupportsPassword`, `SupportsMfa`, `SupportsClientProvisioning`, `SupportsBootstrap`). Authority Core uses these flags when wiring flows such as the password grant, bootstrap APIs, and client provisioning. Built-in plugins (e.g., Standard) will fail fast or force-enable required capabilities if the descriptor is misconfigured, so keep manifests accurate. - Typical configuration (`etc/authority.plugins/standard.yaml`): ```yaml plugins: descriptors: standard: assemblyName: "StellaOps.Authority.Plugin.Standard" capabilities: - password - bootstrap ``` - Only declare a capability if the plug-in genuinely implements it. For example, if `SupportsClientProvisioning` is `true`, the plug-in must supply a working `IClientProvisioningStore`. **Operational reminder:** the Authority host surfaces capability summaries during startup (see `AuthorityIdentityProviderRegistry` log lines). Use those logs during smoke tests to ensure manifests align with expectations. **Configuration path normalisation:** Manifest-relative paths (e.g., `tokenSigning.keyDirectory: "../keys"`) are resolved against the YAML file location and environment variables are expanded before validation. Plug-ins should expect to receive an absolute, canonical path when options are injected. **Password policy guardrails:** The Standard registrar logs a warning when a plug-in weakens the default password policy (minimum length or required character classes). Keep overrides at least as strong as the compiled defaults—operators treat the warning as an actionable security deviation. ## 4. Project Scaffold - Target **.NET 10 preview**, enable nullable, treat warnings as errors, and mark Authority plug-ins with `true`. - Minimum references: - `StellaOps.Authority.Plugins.Abstractions` (contracts & capability helpers) - `StellaOps.Plugin` (hosting/DI helpers) - `StellaOps.Auth.*` libraries as needed for shared token utilities (optional today). - Example `.csproj` (trimmed from `StellaOps.Authority.Plugin.Standard`): ```xml net10.0 enable true true ``` (Add other references—e.g., MongoDB driver, shared auth libraries—according to your implementation.) ## 5. Implementing `IAuthorityPluginRegistrar` - Create a parameterless registrar class that returns your plug-in type name via `PluginType`. - Use `AuthorityPluginRegistrationContext` to: - Bind options (`AddOptions(pluginName).Bind(...)`). - Register singletons for stores/enrichers using manifest metadata. - Register any hosted bootstrap tasks (e.g., seed admin users). - Always validate configuration inside `PostConfigure` and throw meaningful `InvalidOperationException` to fail fast during startup. - Use the provided `ILoggerFactory` from DI; avoid static loggers or console writes. - Example skeleton: ```csharp internal sealed class MyPluginRegistrar : IAuthorityPluginRegistrar { public string PluginType => "my-custom"; public void Register(AuthorityPluginRegistrationContext context) { var name = context.Plugin.Manifest.Name; context.Services.AddOptions(name) .Bind(context.Plugin.Configuration) .PostConfigure(opts => opts.Validate(name)); context.Services.AddSingleton(sp => new MyIdentityProvider(context.Plugin, sp.GetRequiredService(), sp.GetRequiredService(), sp.GetRequiredService>())); } } ``` ## 6. Identity Provider Surface - Implement `IIdentityProviderPlugin` to expose: - `IUserCredentialStore` for password validation and user CRUD. - `IClaimsEnricher` to append roles/attributes onto issued principals. - Optional `IClientProvisioningStore` for machine-to-machine clients. - `AuthorityIdentityProviderCapabilities` to advertise supported flows. - Password guidance: - Standard plug-in hashes via `ICryptoProvider` using Argon2id by default and emits PHC-compliant strings. Successful PBKDF2 logins trigger automatic rehashes so migrations complete gradually. See `docs/security/password-hashing.md` for tuning advice. - Enforce password policies before hashing to avoid storing weak credentials. - Health checks should probe backing stores (e.g., Mongo `ping`) and return `AuthorityPluginHealthResult` so `/ready` can surface issues. - When supporting additional factors (e.g., TOTP), implement `SupportsMfa` and document the enrolment flow for resource servers. ### 6.1 Bootstrap lifecycle Standard plug-in installs begin with an operator-provided manifest and secrets bundle. The registrar validates those inputs, primes the credential store, and only then exposes the identity surface to the host. Every transition is observable (audit events + telemetry) and deterministic so air-gapped operators can replay the bootstrap evidence. - Secrets bundles must already contain hashed bootstrap principals. Registrars re-hash only to upgrade algorithms (e.g., PBKDF2 to Argon2id) and log the outcome. - `WarmupAsync` should fail fast when Mongo indexes or required secrets are missing; readiness stays `Unhealthy` until the registrar reports success. - Audit and telemetry payloads (`authority.plugin.load`) are mirrored into Offline Kits so security reviewers can verify who seeded credentials and when. ![Standard plug-in bootstrap sequence](../assets/authority/authority-plugin-bootstrap-sequence.svg) _Source:_ `docs/assets/authority/authority-plugin-bootstrap-sequence.mmd` ### 6.2 Credential audit telemetry (SEC2/SEC3) - Password verification now emits `authority.plugin.standard.password_verification` records through the shared `IAuthEventSink`. `StandardCredentialAuditLogger` converts every outcome (success, lockout, password reset, MFA requirement) into `AuthEventRecord` instances so `/token` observability can be correlated with plugin activity. - `IAuthorityCredentialAuditContextAccessor` captures the caller’s correlation ID, client ID, tenant, remote IP, forwarded addresses, and user agent. OpenIddict handlers push a scope right before invoking the plug-in, and the logger automatically copies those fields into the audit event: ```csharp using var scope = auditContextAccessor.BeginScope(new AuthorityCredentialAuditContext( correlationId, clientId, tenantId, rateLimiterMetadata?.RemoteIp, rateLimiterMetadata?.ForwardedFor, rateLimiterMetadata?.UserAgent)); ``` - Outcome mapping is deterministic: `AuthorityCredentialFailureCode.LockedOut` ⇒ `AuthEventOutcome.LockedOut`, `RequiresPasswordReset`/`PasswordExpired` ⇒ `RequiresFreshAuth`, and `RequiresMfa` ⇒ `RequiresMfa`. Anything else falls back to `Failure`. - Lockout/rate-limit telemetry is carried via structured properties so SOC dashboards can slice the data: - `plugin.failed_attempts` – running count prior to the current decision. - `plugin.failed_attempts_cleared` – how many failures were cleared after a successful login. - `plugin.lockout_until` – ISO‑8601 timestamp showing when the account unlocks (classified as `Personal`). - `plugin.retry_after_seconds` – ceiling of `AuthorityCredentialVerificationResult.RetryAfter.TotalSeconds`; surfaced on both the audit event and the verification result to guide HTTP 429/423 responses. - `plugin.rehashed` – algorithm tag (`argon2id`) when a legacy hash is upgraded. - `plugin.failure_code` – enum name corresponding to the failure classification. - Remember that everything you add to `AuthorityCredentialVerificationResult.AuditProperties` flows into both the `/token` audit event and the plug-in–scoped event above, so keep names stable and values deterministic for Offline Kit replay. - **Mongo2Go prerequisite:** the plugin test suite relies on Mongo2Go’s embedded `mongod`. Export the OpenSSL 1.1 shim described in `docs/19_TEST_SUITE_OVERVIEW.md` (section “Mongo2Go / OpenSSL shim”) before running `dotnet test`, e.g.: ```bash export LD_LIBRARY_PATH=\"$(pwd)/tests/native/openssl-1.1/linux-x64:${LD_LIBRARY_PATH:-}\" ``` Without this step the embedded server fails to launch on OpenSSL 3 systems, causing timeouts in `StandardUserCredentialStoreTests`. ### 6.3 Plugin-specific mitigations (SEC5.PLG) - Bootstrap seed users default to `RequirePasswordReset = true`. `StandardUserCredentialStore.EnsureBootstrapUserAsync` enforces the configured password policy, rejects partial credentials, and emits `authority.plugin.load` telemetry so operators can prove who seeded the initial principals. - Password policy overrides are validated against a built-in baseline (min length 12 + mixed character classes). The registrar now logs a structured warning whenever a deployment attempts to weaken those defaults, giving security reviewers an audit breadcrumb and satisfying the SEC5.PLG threat-model requirement. - All bootstrap and password operations use `ICryptoProvider` + Argon2id; legacy PBKDF2 hashes are upgraded inline and tagged via `plugin.rehashed`. Document any deviations so downstream plug-ins (or auditors) can reason about entropy expectations. - Lockout metadata is deterministic: `plugin.lockout_until` + `plugin.retry_after_seconds` form the authoritative signal for incident response, and their presence is now noted in the Authority threat model (`docs/security/authority-threat-model.md`). - When extending the Standard plug-in (or authoring a new one), keep these mitigations intact: enforce baseline policies, require explicit password reset flags on bootstrap flows, and emit the audit properties listed above. Third‑party plugins are expected to follow the same contract before they can advertise `SupportsPassword` or `SupportsBootstrap`. ### 6.4 LDAP plug-in quick reference (PLG7.IMPL-005) - **Mutual TLS & trust stores.** `security.requireTls=true` enforces LDAPS/start‑TLS; set `security.requireClientCertificate=true` to demand mutual TLS. When that flag is enabled you must supply `connection.clientCertificate.pfxPath` + `passwordSecret`. Bundle CA chains under `connection.trustStore.bundlePath` and keep the files inside Offline Kit paths (`plugins/authority/ldap/**`) so air-gapped installs can import them without editing manifests. - **DN‑to‑role mapping.** `claims.groupToRoleMap` is ideal for static DNs (e.g. `cn=stellaops-admins,...` → `operators`). Regex mappings let you project portions of the DN into role names: define `pattern` with named captures (`(?P...)`) and use `{role}` placeholders in `roleFormat`. The enricher sorts all emitted roles, dedupes, and adds them as `ClaimTypes.Role`. - **Attribute pass-through.** `claims.extraAttributes` pairs the outgoing claim name with the LDAP attribute to read (first value wins). Only non-empty strings are written, which keeps audit/compliance data deterministic. - **Mongo claims cache.** `claims.cache.enabled=true` wires the `MongoLdapClaimsCache` (default collection `ldap_claims_cache_`). Set `ttlSeconds` according to your directory freshness SLA and adjust `maxEntries` to cap disk usage; eviction is deterministic (oldest entries removed first). Offline Kit bundles now include the collection name requirements so replicas can pre-create capped collections. - **Client provisioning audit mirror.** `clientProvisioning.auditMirror.enabled=true` persists every LDAP write into Mongo (`ldap_client_provisioning_` by default) with `{operation, dn, tenant, project, secretHash}`. That mirror is shipped in Offline Kits so regulators can diff LDAP state even without directory access. When `clientProvisioning.enabled=false`, the registrar logs a warning and downgrades the capability at runtime. - **Bootstrap seeding + audits.** `bootstrap.*` mirrors the provisioning contract for human operators: the plug-in writes `uid={username}` entries under `bootstrap.containerDn`, applies `staticAttributes` placeholders (`{username}`, `{displayName}`), and mirrors deterministic audit documents to Mongo (`ldap_bootstrap_` by default) with hashed secrets (`AuthoritySecretHasher`). Bootstrap only lights up when (1) the manifest advertises the capability, (2) `bootstrap.enabled=true`, **and** (3) the plug-in proves the bind account can add/delete under the configured container. Otherwise the capability is silently downgraded and health checks surface `capabilities=bootstrapDisabled`. - **Capability proofing.** On startup the plug-in performs a short-lived LDAP write probe (add→delete) inside each configured container. If either probe fails, the respective capability (`clientProvisioning`, `bootstrap`) is removed, `ClientProvisioning` stays `null`, and `CheckHealthAsync` reports `Degraded` until permissions are restored. This keeps read-only deployments safe while making it obvious when operators still need to grant write scope. - **Sample manifest + binaries.** The curated manifest lives at `etc/authority.plugins/ldap.yaml` and demonstrates TLS, regex mappings, caching, and audit mirror options. Offline Kits copy both the manifest and the compiled plug-in into `plugins/authority/StellaOps.Authority.Plugin.Ldap/` so operators can drop them straight into air-gapped composer deployments. ## 7. Configuration & Secrets - Authority looks for manifests under `etc/authority.plugins/`. Each YAML file maps directly to a plug-in name. - Support environment overrides using `STELLAOPS_AUTHORITY_PLUGINS__DESCRIPTORS____...`. - Never store raw secrets in git: allow operators to supply them via `.local.yaml`, environment variables, or injected secret files. Document which keys are mandatory. - Validate configuration as soon as the registrar runs; use explicit error messages to guide operators. The Standard plug-in now enforces complete bootstrap credentials (username + password) and positive lockout windows via `StandardPluginOptions.Validate`. - Cross-reference bootstrap workflows with `docs/modules/authority/operations/bootstrap.md` (to be published alongside CORE6) so operators can reuse the same payload formats for manual provisioning. - `passwordHashing` inherits defaults from `authority.security.passwordHashing`. Override only when hardware constraints differ per plug-in: ```yaml passwordHashing: algorithm: Argon2id memorySizeInKib: 19456 iterations: 2 parallelism: 1 ``` Invalid values (≤0) fail fast during startup, and legacy PBKDF2 hashes rehash automatically once the new algorithm succeeds. ### 7.1 Token Persistence Contract - The host automatically persists every issued principal (access, refresh, device, authorization code) in `authority_tokens`. Plug-in code **must not** bypass this store; use the provided `IAuthorityTokenStore` helpers when implementing custom flows. - When a plug-in disables a subject or client outside the standard handlers, call `IAuthorityTokenStore.UpdateStatusAsync(...)` for each affected token so revocation bundles stay consistent. - Supply machine-friendly `revokedReason` codes (`compromised`, `rotation`, `policy`, `lifecycle`, etc.) and optional `revokedMetadata` entries when invalidating credentials. These flow straight into `revocation-bundle.json` and should remain deterministic. - Token scopes should be normalised (trimmed, unique, ordinal sort) before returning from plug-in verification paths. `TokenPersistenceHandlers` will keep that ordering for downstream consumers. ### 7.2 Claims & Enrichment Checklist - Authority always sets the OpenID Connect basics: `sub`, `client_id`, `preferred_username`, optional `name`, and `role` (for password flows). Plug-ins must use `IClaimsEnricher` to append additional claims in a **deterministic** order (sort arrays, normalise casing) so resource servers can rely on stable shapes. ### Claims enrichment & caching contracts LDAP/AD plug-ins now expose first-class `claims.*` configuration to keep enrichment consistent: - `claims.groupAttribute`, `claims.groupToRoleMap`, and `claims.regexMappings` translate directory DNs into Authority roles. Regex mappings honour both .NET-style `(?)` and Python-style `(?P)` capture syntax; names become `{role}` placeholders inside `roleFormat`. - `claims.extraAttributes` is a deterministic map of `{ claimName: ldapAttribute }`. Only the first attribute value is propagated and plug-ins must skip null/empty payloads. - `claims.cache.*` enables a Mongo-backed cache (`ldap_claims_cache_` by default) with TTL + capacity trims so repeated password grants avoid hammering the directory. TTL must be > 0 seconds and max entries ≥ 0. Collection names are normalised to lowercase ASCII and strip `/`, `\`, and `:` to remain Offline-Kit friendly. When the cache is disabled, plug-ins inject `DisabledLdapClaimsCache` so the enricher path stays free of null checks. Cache documents must stay tenant-scoped and include `cachedAt`/`expiresAt` so operators can audit freshness. See `StellaOps.Authority.Plugin.Ldap.Claims` for the reference implementation. - Recommended enrichment keys: - `stellaops.realm` – plug-in/tenant identifier so services can scope policies. - `stellaops.subject.type` – values such as `human`, `service`, `bootstrap`. - `groups` / `projects` – sorted arrays describing operator entitlements. - Claims visible in tokens should mirror what `/token` and `/userinfo` emit. Avoid injecting sensitive PII directly; mark values with `ClassifiedString.Personal` inside the plug-in so audit sinks can tag them appropriately. - For client-credential flows, remember to enrich both the client principal and the validation path (`TokenValidationHandlers`) so refresh flows keep the same metadata. ### Client provisioning & audit mirror - `clientProvisioning.enabled` must be true for the LDAP plug-in to expose `IClientProvisioningStore` and advertise the `clientProvisioning` capability. If the manifest lists the capability but the config disables it, startup logs a warning and the capability stays off. - `clientProvisioning.containerDn` is the base DN for machine/service accounts; the plug-in automatically builds RDNs as `=` (default `cn`) and escapes special characters to remain RFC 4514 compliant. - `clientProvisioning.secretAttribute` controls which LDAP attribute stores the client secret; the run-time writes the cleartext secret you pass during provisioning, while Mongo keeps only the hashed reference for audit (`AuthoritySecretHasher`). - `clientProvisioning.auditMirror.*` persists deterministic Mongo documents (default collection `ldap_client_provisioning_`) capturing `{operation, dn, tenant, project, secretHash}` so operators can diff LDAP state even in air-gaps. - LDAP writes bind with the configured service account (`connection.bindDn` + secret). If the account loses modify permissions the store returns `ldap_error` and no Mongo state is changed, giving operators a single place to investigate. ### 7.3 Revocation Bundles & Reasons - Use `IAuthorityRevocationStore` to record subject/client/token revocations when credentials are deleted or rotated. Stick to the standard categories (`token`, `subject`, `client`, `key`). - Include a deterministic `reason` string and optional `reasonDescription` so operators understand *why* a subject was revoked when inspecting bundles offline. - Plug-ins should populate `metadata` with stable keys (e.g., `revokedBy`, `sourcePlugin`, `ticketId`) to simplify SOC correlation. The keys must be lowercase, ASCII, and free of secrets—bundles are mirrored to air-gapped agents. ## 8. Rate Limiting & Lockout Interplay Rate limiting and account lockouts are complementary controls. Plug-ins must surface both deterministically so operators can correlate limiter hits with credential rejections. **Baseline quotas** (from `docs/dev/authority-rate-limit-tuning-outline.md`): | Endpoint | Default policy | Notes | |----------|----------------|-------| | `/token` | 30 requests / 60s, queue 0 | Drop to 10/60s for untrusted ranges; raise only with WAF + monitoring. | | `/authorize` | 60 requests / 60s, queue 10 | Reduce carefully; interactive UX depends on headroom. | | `/internal/*` | Disabled by default; recommended 5/60s when enabled | Keep queue 0 for bootstrap APIs. | **Retry metadata:** The middleware stamps `Retry-After` plus tags `authority.client_id`, `authority.remote_ip`, and `authority.endpoint`. Plug-ins should keep these tags intact when crafting responses or telemetry so dashboards remain consistent. **Lockout counters:** Treat lockouts as **subject-scoped** decisions. When multiple instances update counters, reuse the deterministic tie-breakers documented in `src/DEDUP_CONFLICTS_RESOLUTION_ALGO.md` (freshness overrides, precedence, and stable hashes) to avoid divergent lockout states across replicas. **Alerting hooks:** Emit structured logs/metrics when either the limiter or credential store rejects access. Suggested gauges include `aspnetcore_rate_limiting_rejections_total{limiter="authority-token"}` and any custom `auth.plugins..lockouts_total` counter. ![Authority rate limit and lockout flow](../assets/authority/authority-rate-limit-flow.svg) _Source:_ `docs/assets/authority/authority-rate-limit-flow.mmd` ## 9. Logging, Metrics, and Diagnostics - Always log via the injected `ILogger`; include `pluginName` and correlation IDs where available. - Activity/metric names should align with `AuthorityTelemetry` constants (`service.name=stellaops-authority`). - Expose additional diagnostics via structured logging rather than writing custom HTTP endpoints; the host will integrate these into `/health` and `/ready`. - Emit metrics with stable names (`auth.plugins..*`) when introducing custom instrumentation; coordinate with the Observability guild to reserve prefixes. ## 10. Testing & Tooling - Unit tests: use Mongo2Go (or similar) to exercise credential stores without hitting production infrastructure (`StandardUserCredentialStoreTests` is a template). - Determinism: fix timestamps to UTC and sort outputs consistently; avoid random GUIDs unless stable. - Smoke tests: launch `dotnet run --project src/Authority/StellaOps.Authority/StellaOps.Authority` with your plug-in under `StellaOps.Authority.PluginBinaries` and verify `/ready`. - Example verification snippet: ```csharp [Fact] public async Task VerifyPasswordAsync_ReturnsSuccess() { var store = CreateCredentialStore(); await store.UpsertUserAsync(new AuthorityUserRegistration("alice", "Pa55!", null, null, false, Array.Empty(), new Dictionary()), CancellationToken.None); var result = await store.VerifyPasswordAsync("alice", "Pa55!", CancellationToken.None); Assert.True(result.Succeeded); Assert.True(result.User?.Roles.Count == 0); } ``` ## 11. Packaging & Delivery - Output assembly should follow `StellaOps.Authority.Plugin..dll` so the host’s search pattern picks it up. - Place the compiled DLL plus dependencies under `StellaOps.Authority.PluginBinaries` for offline deployments; include hashes/signatures in release notes (Security Guild guidance forthcoming). - Document any external prerequisites (e.g., CA cert bundle) in your plug-in README. - Update `etc/authority.plugins/.yaml` samples and include deterministic SHA256 hashes for optional bootstrap payloads when distributing Offline Kit artefacts. [^ldap-rfc]: Lightweight Directory Access Protocol (LDAPv3) specification — [RFC 4511](https://datatracker.ietf.org/doc/html/rfc4511). ## 12. Checklist & Handoff - ✅ Capabilities declared and validated in automated tests. - ✅ Bootstrap workflows documented (if `bootstrap` capability used) and repeatable. - ✅ Local smoke test + unit/integration suites green (`dotnet test`). - ✅ Operational docs updated: configuration keys, secrets guidance, troubleshooting. - Submit the developer guide update referencing PLG6/DOC4 and tag DevEx + Docs reviewers for sign-off. --- Mermaid sources for the embedded diagrams live under `docs/assets/authority/`. Regenerate the SVG assets with your preferred renderer before committing future updates so the visuals stay in sync with the `.mmd` definitions.