Skip to content

ADR-0009: Catalogs and access rules as code

Status: Accepted — 2026-09-23. The project owner approved this decision. Acceptance establishes the architectural contract; it does not establish a completed implementation or successful execution of outstanding checks.

Accepted refinement — 2026-09-24: owner configuration separately selects machine exposure, task issuance and the required execution boundary. The static-catalog baseline remains; extension declarations never grant these rights. The original acceptance and unrun checks are preserved.

English | Русский

Rukh must serve small open teams and large organizations. A team sharing a few extensions should not need an identity server, permission service, database or runtime configuration language. An organization must be able to add those integrations without changing extension declarations or creating another loading and access model.

Catalogs, group membership and permission rules can be maintained as code. Their authoring and delivery are separate from identity verification, access evaluation and artifact installation. This ADR refines the selected boundaries in ADR-0005–0008; it does not classify extensions by connectivity or authorization frequency.

  • A useful baseline with one static catalog and explicit built-in local policy.
  • Independent additions of groups, authenticated identity and external services.
  • The same discovery, command identity and access checks for every configuration.
  • Reviewed, traceable configuration without executing downloaded code.
  • Clear ownership of policy and publisher trust; extension authors cannot grant rights.
OptionBenefitLimitation
Require identity and permission servers everywhereOne service-based deployment modelUnnecessary administration for small teams and public tools
Separate a simplified CLI from a corporate CLIEach can optimize its first use caseDivergent semantics, extension contracts and migration paths
Local declarations and replaceable providers behind common contractsSmall baseline and independent integrationsRequires explicit trust, identity and configuration-version boundaries

Use a static catalog and built-in local access evaluation as the baseline. Treat sign-in, group declarations, KCL authoring and external services as independently selectable capabilities. The loader and core retain the same contracts.

A CLI owner can ship one validated JSON catalog with the CLI or supply an explicitly approved local file. It contains extension identities, allowed release references and artifact locations. A built-in public-catalog policy, explicitly selected by the owner, permits discovery, installation and invocation of the approved entries without organizational sign-in. It still applies publisher trust, compatibility, declared requirements and command checks. Protected resource operations require their own explicit grants; catalog inclusion does not grant every operation requested by an extension.

The public-catalog preset authorizes the distinct discover, install, activate and execute lifecycle actions for catalog-admitted exact releases in its owner-bound local access domain, including built-in base-help visibility. Each action still passes its own trust, compatibility and lifecycle checks. Beyond the local presentation scope below, it does not authorize contextual describe, protected resource operations, runtime installation, another access domain, unlisted releases or changes to owner configuration. Those require explicit rules or owner-approved preparation policy. A catalog’s version range only selects among independently verified, admitted releases; it is not approval of arbitrary bytes under the same name. Removing an entry changes this preset’s grants when the new configuration is applied, without claiming to undo completed actions or forcibly stop ordinary processes.

The trusted built-in preset also supplies the ordinary presentation scope: bounded command output and diagnostic logging, and declared, supported standard dialogs or managed views in the current execute invocation’s approved terminal under ADR-0002. A small team need not write separate permission rules just to display output, report progress or ask a question. Feature negotiation, input ownership, secret handling, owner-selected log destinations, quotas, cancellation and noninteractive errors still apply. This permits only registered standard presentation facilities for that invocation; it does not grant arbitrary core operations, custom component registration, other invocations’ input, credential retrieval, or file/network actions and destinations chosen by an extension. A confirmation or answer never authorizes the resource action it describes.

This configuration requires no group files, identity account, remote broker, database, KCL installation or CI system. The owner can validate and publish the catalog manually. A Git repository or static HTTPS hosting can distribute it; neither turns installation into execution of repository scripts. Missing or invalid policy is an error, not an implicit public-catalog selection.

Without sign-in the core records an explicit unauthenticated/local context. It does not invent a verified corporate user. Rules requiring a verified subject cannot match that context. Optional installed runtimes remain the trusted preinstalled-provider case from ADR-0008; a runtime manager is not a prerequisite for a native extension.

These are examples of compositions, not mandatory maturity levels or extension execution modes. A large company may use static catalogs, and a small team may use organizational sign-in.

ConfigurationCatalogIdentity and access
Public toolsBundled or approved static catalogExplicit public/local policy; no organizational sign-in
Team rules maintained in GitStatic catalog and reviewed group/rule declarationsVerified identity where individual access is needed; built-in rule evaluator
Existing organization systemsStatic or service-provided catalogStandard identity plus local declarations, a standard permission API or a custom adapter

The owner selects the authoritative provider for each access domain. Using local declarations is a deliberate configuration, never a fallback when a required remote provider fails. A policy change cannot remove an owner-bound broker requirement. Standards and custom integrations retain ADR-0005’s identity and failure rules.

The component view shows interchangeable sources behind shared responsibilities. Only configured providers are used; multiple incoming options do not mean combining their permissions.

Accepted simple and service-based configuration
Accepted simple and service-based configurationStatic data and external services can supply catalog and access roles independently. Identity is verified when rules require it. Discovery and permission evaluation share the core boundary, while the loader still verifies releases.Validated catalog and rulesDiscovery and accessboundaryStandard or custom servicesVerified identity whenrequiredLoader and releaseverificationCore operation checksLocal data providersRegistered adaptersBound subjectPermitted referencesAccess decisionAccepted simple and service-based configurationStatic data and external services can supply catalog and access roles independently. Identity is verified when rules require it. Discovery and permission evaluation share the core boundary, while the loader still verifies releases.Validated catalog and rulesDiscovery and accessboundaryStandard or custom servicesVerified identity whenrequiredLoader and releaseverificationCore operation checksLocal data providersRegistered adaptersBound subjectPermitted referencesAccess decision
DataRequired meaning and owner
CatalogAuthority-qualified extension identity, permitted release selection or exact release reference, approved source binding and required access domain; CLI owner controls admission
Group declarationsGroup IDs and membership by verified authority/subject references or an explicitly configured trusted directory mapping; managed by the access-domain owner
Access rulesSubjects/groups, actions, resource scopes and allow/deny effects under one authority; stable rule IDs for explanations
Configuration versionSchema versions, configuration authority, exact catalog/group/policy revisions and digests, and their compatibility bindings

These logical documents can be kept in one file, separated within one repository or maintained independently with explicit version references. The minimal catalog uses the built-in policy and has no group document. Catalog format, extension descriptor format and permission schema are separate: a catalog admits an extension, ADR-0006 describes that extension, and policy decides what can be done.

JSON is the client data format. Small catalogs and rules may be authored directly as JSON; optional KCL schemas compile to the same validated data on the owner/publisher side. KCL is useful for reuse and structural checks, but is not required to consume a catalog or evaluate access. The client does not execute fetched KCL, Git hooks or policy scripts. General-purpose policy languages, if needed, belong to a separately registered trusted provider, not executable expressions in the baseline format.

The selected data formats are rukh.catalog/1, rukh.groups/1 and rukh.policy/1. The suffix selects the schema major version, not a released product version. These are separate logical schemas even when a single catalog file embeds its local policy, optional groups and approved sources. The compact form must explicitly contain policy: { "preset": "public-catalog" } or refer to the policy explicitly selected in trusted owner configuration; omission never selects public access. A multi-domain configuration binds separate policies through the owner profile rather than merging their allows.

For compact single-domain input, the catalog declares a defaultAccessDomain. Inline groups/policy and entries may inherit that exact domain; inline groups/policy inherit the catalog’s configuration authority and their fixed /1 schema. Inheritance is only this documented shorthand: the validator expands it before checking complete records. Explicit conflicting values are rejected. Standalone documents contain their own headers, and multi-domain records state their domain explicitly. A group document may be absent, but an explicit policy choice may not.

Document/fieldRequired representation and validation
Catalog headerschema, catalogId, configuration authority, and entries. The authority must match the owner-approved configuration publisher, not a value trusted because it appears in the file.
Catalog entryextension: { authority, package } using ADR-0006 identity rules; accessDomain; and a nonempty set of releases or a declared release-selection constraint bound to an approved source. Duplicate extension entries or conflicting authority/domain bindings are invalid.
Exact release referenceversion, releaseDigest, sourceId and source-specific locator under ADR-0007. A release digest is lowercase sha256: followed by 64 hexadecimal digits and identifies exact envelope bytes. A selection constraint has an explicit version scheme; resolution records one exact release before installation.
Source bindingsourceId, kind and approved repository/registry/origin plus its bounded locator rules. Credentials are protected references in owner settings, never source URLs containing secrets. A catalog may narrow an approved source; admitting a new source requires explicit owner approval of that binding.
Groups documentschema, authority, accessDomain, groups. Each group has a unique id and members containing explicit verified principal references. A declared trusted directory mapping is an alternative input provider, not an unverified username list.
Policy documentschema, authority, accessDomain, and exactly one of the named preset or rules. Rules have unique id, effect: allow / deny, nonempty subject selectors, known actions and explicit resource selectors. Mixing a preset and extra rules is invalid; customization selects an explicit rules document.
Document referenceApproved source/path, exact whole-document digest and byte size, plus an optional JSON Pointer to an embedded logical document. Hashing uses exact UTF-8 file bytes; the client does not reserialize before verification. External references resolve to immutable records, never a mutable branch used as a revision.
Configuration validityAn explicit pinned selection, or an owner-registered validity contract and its required settings. The baseline is pinned. A provider requiring stronger freshness cannot be configured without a supported validity contract; an unknown contract is an error.

Catalog, source, group and rule IDs use ADR-0006’s local identifier grammar: lowercase ASCII, at most 64 characters, [a-z][a-z0-9]*(?:[.-][a-z0-9]+)*. References retain the owning authority/domain; a local group ID cannot refer to another authority by coincidence. Package versions and publisher namespaces use ADR-0006, while identity issuers/subjects use the registered identity provider’s rules from ADR-0005. Human labels are optional bounded text and never identifiers. Action IDs resolve to trusted registered action contracts; a string in this file cannot create a protected operation.

Validation rejects unknown schema versions, unknown fields outside a declared metadata extension point, duplicate JSON keys, invalid Unicode, nonfinite or unsafe numeric values, duplicate IDs and dangling references. Security-relevant selectors cannot be hidden in ignored metadata. A default owner configuration permits at most 8 MiB of decoded JSON across one resolved configuration, 32 referenced files, reference depth 8, JSON depth 32, 10,000 catalog entries, 10,000 rules and 50,000 total explicit group members. References form an acyclic graph. Each human label is at most 1 KiB of UTF-8. Higher finite limits require explicit owner configuration and implementation support; a publisher cannot request them. Limits apply before activation and before unbounded allocation; an oversized or malformed configuration leaves the active profile unchanged. Unknown required subjects/attributes cannot be normalized to null or an empty group during validation.

Catalog records resolve through approved source bindings to ADR-0007 releases. An arbitrary URL, untrusted name or Git branch is not proof of publisher authority. A branch/tag may be a configured update locator, but an activated configuration records exact content and source revision. The catalog can point to OCI, HTTPS or Git artifacts independently of where the catalog itself is stored.

Group membership is an authorization input, not authentication. A person can be declared by an identity authority and stable subject identifier; an OIDC mapping uses issuer plus subject as in ADR-0005. Display names, unverified email, CLI flags and environment variables do not prove identity. Creating accounts, storing passwords and issuing sign-in tokens are outside these declarations. Existing directory membership may be mapped explicitly instead of copying every person into Git.

The baseline evaluator uses explicit subject/group membership, known actions and validated resource scopes. No matching grant means denial; a matching explicit denial overrides grants within that policy. Unknown required attributes or invalid rules do not become unrestricted access. Missing required identity produces an authentication requirement, not a match to an arbitrary user. Catalog visibility, installation, invocation and protected core operations remain separate actions. Command aliases resolve to the same canonical command ID and rights.

Subject selectors are tagged values: any for any context inside that domain, local for an explicit local context, an exact user or service authority/subject pair, or group for a declared group in that domain. any does not invent a verified identity; a protected resource can independently require one. The initial group schema has no nested groups, computed expressions or inferred membership. Unknown groups reject the configuration; unavailable required directory data makes evaluation indeterminate rather than treating everyone as a member or ignoring a possible denial.

Resource matching uses the selected action’s registered scope schema: exact canonical IDs, finite sets, or an explicitly declared all within that action’s bound domain and owner limits. No omitted selector means a wildcard. The baseline has no arbitrary scripts, regex permissions, implicit URI-prefix matching or user-supplied authorization expressions. Missing required resource attributes cannot satisfy a rule. Validated operation inputs determine the actual resource under ADR-0002; extension-supplied labels do not override it.

Evaluation first validates context, action and resource, then resolves required identity/group inputs, then applies matching explicit denials and grants. Any uncertainty needed to rule out a denial prevents an allow. A complete evaluation with no matching grant returns deny; missing required authentication returns authentication_required; unavailable required membership returns indeterminate. A matching denial remains a denial even if another grant matches. This order prevents incomplete membership data from hiding a restrictive rule.

The evaluator returns the existing access result with the configuration revision and applicable rule IDs for trusted diagnostics/audit. It cannot exceed owner limits or substitute a different subject/resource. The same rules filter discovery and answer operation checks; a filtered catalog is not a durable grant. There is one authoritative decision provider per access domain, not an implicit union of local and remote permissions.

Private people/group data need not be distributed to CLI users. A service may evaluate the same reviewed declarations and return only permitted catalog references and decisions. Public catalog publication does not automatically publish group membership or private policy. Passwords, access tokens and service secrets are never configuration content.

A simple catalog is validated and explicitly approved by its owner. A repository with several related documents can produce one immutable configuration version linking their exact bytes. Git review and CI are useful publication mechanisms, not required services or substitutes for runtime validation. The configuration package is data and cannot register adapter code or change its own trusted publisher.

The publication view covers a multi-document configuration and the shared activation boundary from ADR-0007. For a single catalog, the group/rule inputs are absent and the selected built-in policy is recorded instead. The retained command generation must still pass validation against the candidate owner configuration.

Accepted declaration publication and application
Accepted declaration publication and applicationOwner-maintained JSON or KCL becomes validated catalog, group and rule data. Approval publishes one exact configuration version. A consumer verifies authority, references and the retained or rebuilt command generation, then commits one active profile. Invalid candidates leave the active profile unchanged without extending its validity.Owner-maintained JSON orKCLCatalog and optionalgroup/rule dataExact configuration versionCandidate valid?Retained or rebuilt commandgenerationAtomically commit activeprofileReject candidate and reporterrorValidate and exportOwner approvalCheck authority, digests andreferencesRevalidate owner bindings androutesYesNoAccepted declaration publication and applicationOwner-maintained JSON or KCL becomes validated catalog, group and rule data. Approval publishes one exact configuration version. A consumer verifies authority, references and the retained or rebuilt command generation, then commits one active profile. Invalid candidates leave the active profile unchanged without extending its validity.Owner-maintained JSON orKCLCatalog and optionalgroup/rule dataExact configuration versionCandidate valid?Retained or rebuilt commandgenerationAtomically commit activeprofileReject candidate and reporterrorValidate and exportOwner approvalCheck authority, digests andreferencesRevalidate owner bindings androutesYesNo

Consumers verify schema, limits, reference integrity, configured authority and source trust independently. The simple trust path is an owner-shipped catalog or an explicitly approved content digest kept in trusted owner configuration. The digest must come from that trusted configuration, not from the same unauthenticated download it purports to verify. Signed metadata and centrally managed trust can be added when needed; a signing service or key infrastructure is not mandatory for the baseline. Extensions and catalogs cannot replace owner trust roots themselves.

ownerConfigRevision identifies one immutable record binding exact catalog/group/policy documents, owner limits, source trust and selected provider registrations/configuration. configRevision in ADR-0005 is an alias for that same identifier. The active state is the single activeProfileRevision from ADR-0007, containing both ownerConfigRevision and commandGeneration. These are not independent active pointers. A policy-only change may reuse a command generation after revalidating its routes, release admissions, authority and owner bindings; otherwise it constructs a compatible generation or fails without partially applying either half. Catalog inclusion alone does not install or activate commands.

The loader commits that pair through ADR-0007’s profile transaction and recovery record. New lookups read one committed profile and pin their command/release from it. An invalid update leaves the active pair unchanged and never prolongs its permitted use. Concurrent changes revalidate against the winning profile before committing; they cannot overwrite its policy or commands with an independently prepared stale half.

Each discovery traversal retains one configRevision and principal binding. Local declaration providers support ADR-0005’s snapshot mode: every page uses the same exact catalog, groups and rules. A service provider may use the explicitly enabled live mode, where immutable client configuration does not imply immutable remote data. Its results must report that limitation; an exhausted live traversal is not advertised as a complete snapshot. A configuration switch invalidates the old continuation with context_invalidated. A caller may explicitly start a new read-only traversal without merging revisions or repeating an external action.

Existing invocations keep their bound identity, release, authority and registered service implementations. Each new protected action uses the applicable current policy revision, limited by the invocation’s original scope. If the new profile no longer admits those bindings, further actions return context_invalidated; they do not acquire another subject/provider or retain a revoked allow. Work already performed is not undone. Decision validity, expiry and any supported caching follow the selected validity contract rather than converting a discovery result or earlier check into a perpetual grant.

Every configuration declares whether it is explicitly pinned or governed by a validity/update policy. A pinned public catalog changes through an explicit configuration update and makes no promise of automatic refresh or immediate revocation. Configurations requiring current membership or bounded revocation must select a provider and validity contract that can meet those requirements. A Git commit or merge alone does not change consumers’ effective permissions. Refresh intervals, disconnected operation and immediate revocation mechanisms remain separate decisions; this ADR does not introduce extension classes for them.

ADR-0012 defines the action-validity contracts and observed-revocation rules. Its pinned-local mode evaluates the active local data; it does not turn a pinned client configuration into an immutable remote policy. Authentication session and credential validity follow the separate ADR-0011 decision. Configuration distribution schedules and artifact trust updates are not inferred from either contract.

Local evaluation governs this CLI’s discovery and core-mediated operations. A user-editable local file is not a security boundary for a remote corporate resource. Protected services enforce their own rights, and ordinary extension processes retain ADR-0001’s OS-access limitations. An organization requiring centrally enforced policy configures a suitably controlled provider; it does not rely on hiding a local rule file.

The owner can replace a static catalog provider with a catalog service, add verified identity to group-based rules, or replace local evaluation with an AuthZEN/custom provider. Such changes are explicit configuration migrations, not failure recovery. Extension/command identities, action semantics, source authority and required access domains must be preserved or explicitly migrated and checked. Existing invocations retain their original bound context; switching providers cannot silently rebind them or carry old grants into a different authority.

Migration is a candidate profile operation. It verifies registrations and identity mappings, resolves the complete candidate documents, checks trust and all required facilities, revalidates the command generation, and only then commits the joint profile. A publisher/catalog cannot initiate an authority migration itself. Moving an extension to another access domain or publisher identity is an explicit owner-approved rebinding with its own admission/trust checks; identical display names or artifact bytes do not make it an update to the same authority. If the new provider does not support the selected consistency or validity contract, the migration fails rather than lowering requirements.

Configuration operations report applied with the committed profile revision, unchanged for the already active identical candidate, or a typed error: invalid_configuration, untrusted_configuration, unsupported_configuration, limit_exceeded, revision_conflict, context_invalidated, unavailable, storage_corrupt, commit_unknown or cancelled. revision_conflict means no candidate commit occurred; reconciliation may build another candidate, but does not replay protected external actions. An uncertain local commit is inspected through ADR-0007’s durable record within a bounded recovery budget. If commitment cannot be established, return commit_unknown, retain its transaction ID and do not claim that the old profile remained active or repeat the update automatically. A confirmed commit remains applied even if cancellation arrives afterwards; cancellation is not a rollback. These are configuration-operation results, not additional command outcomes in ADR-0002.

Refinement: owner profiles for machine use — 2026-09-24

Section titled “Refinement: owner profiles for machine use — 2026-09-24”

The canonical command model may be projected through CLI and an explicitly enabled MCP adapter. Catalog admission and the existing public-catalog preset do not automatically publish every command as a machine tool. The owner selects the machine entry point and its permitted command set, approved task-issuance preset, limits and required execution boundary. These are independent choices: exposure selects what a caller may request, task authority limits what it may do, and confinement determines which restrictions the runtime can enforce.

The smallest machine-enabled configuration can still be one catalog, one approved local preset and protected local state for shared coordination. No sign-in, policy server, JWT issuer, KCL installation or mandatory background job service is added. A trusted native extension may be explicitly exposed with the host-trusted profile and owner limits; this does not claim that file/network access outside core services is confined. A profile requiring stronger confinement is rejected when no registered executor can meet it.

Owner profiles bind allowed task issuance to its source and finite ceilings, including aggregate issuance limits where required. The agent may supply validated operation inputs and request narrower authority; it cannot choose another issuer, actor, subject, policy source or more permissive preset. Text purpose is not a permission rule. Extension manifests may declare requested actions, effects or execution facilities, but cannot add grants, register trusted implementations, switch profiles or weaken required controls.

These choices use supported owner-configuration contracts validated before activation; this refinement does not permit arbitrary new fields in the existing catalog/group/policy /1 documents. Unknown required contracts and unenforceable restrictions remain configuration errors. Their exact task fields and bounds are defined in ADR-0014, their command projection in ADR-0013 and their execution requirements in ADR-0015. All affected settings participate in ownerConfigRevision and the joint profile transaction; no independent active machine-policy pointer is introduced.

Existing tasks pin their issuance ceiling while each new action also obeys current policy. A broader configuration does not enlarge an old grant; a narrowing or invalidation takes effect at final admission under ADR-0012/0014. Policy changes cannot erase reservations for uncertain effects under ADR-0016.

Additional implementation checks remain unrun: enable a bounded local machine profile without remote services; reject undeclared exposure and extension-supplied grants; distinguish explicit trusted-native exposure from required confinement; and race profile updates with task issuance, delegated calls and retained effect reservations. Invalid updates must leave the existing joint profile intact without extending any authority.

  • Small teams need only a catalog and an explicit local policy; infrastructure is added for a concrete requirement.
  • Catalog and access configuration can share a Git workflow while retaining different authorities and publication visibility.
  • Large organizations reuse the same extension, discovery and access contracts with their own services.
  • The selected field contracts, limits and shared activation boundary require executable validators and storage/provider implementations. Simplicity of deployment does not make these checks optional or mean they have been implemented.

The project owner accepted this decision on 2026-09-23. The following list remains required implementation verification, not a claim that these checks passed at acceptance.

For implementation verification, exercise executable schemas and a reference implementation against the selected field contracts, limits and activation rules. Check:

  1. One static catalog and the selected public policy work without KCL, sign-in or a remote service; a missing policy does not select that preset implicitly.
  2. The same extension works with local rules and service-based providers without declaration changes; provider migration preserves identity and required access domains.
  3. Declared people/groups require verified identity where applicable; forged identities, conflicting rules and unknown actions cannot expand access. Discovery does not grant execution or every requested core operation.
  4. Invalid, tampered, mixed-version or interrupted updates never expose a partial profile; policy-only and command updates share one commit boundary. Snapshot pagination cannot combine subjects or revisions, and explicitly selected live results never claim snapshot completeness. Cursor expiry, account changes and traversal limits preserve typed errors.
  5. Public publication excludes private membership and credentials; pinned configuration makes no automatic-revocation claim, and an expired/invalid required policy cannot silently become public/local access.

These functional checks have not been run. This is an accepted design, not an implementation of catalog or policy tooling.

Diagram

Drag to move · + / − to zoom · 0 to fit · Esc to closeDrag to move and pinch to zoom

100%