# Access-Controlled Knowledge Gateway Date: 2026-05-04 ## Purpose The policy gateway adds an explicit boundary before cached query, search, and future context-package results leave a backend. It starts with a local label policy and keeps room for more rigid authorization systems later. This layer does not make Markitect a full identity platform. Core Markitect answers a narrower question: ```text subject + action + object + context -> policy decision ``` Backend and workflow code can then filter, redact, log, and explain results using the same decision envelope. ## Access-Control Ladder The intended progression is: | Level | Mode | Purpose | | --- | --- | --- | | 1 | Labels and trust zones | Local labs, prototypes, and agent context safety. | | 2 | Path/file ACLs | Repository-local restrictions and team boundaries. | | 3 | Relationship policies | Zanzibar/OpenFGA/SpiceDB-style subject-object relations. | | 4 | Attribute/rule policies | OPA/Rego, Cedar, and policy-as-data engines. | | 5 | External policy services | Organization identity, audit, and authorization systems. | WP-0009 implements levels 1 and 2 directly and defines adapter boundaries for levels 3 and 4. ## Local Label Policy Local policy files are YAML: ```yaml id: local-label-policy mode: enforce default_labels: [public] default_subject: public-agent subjects: public-agent: allowed_labels: [public] trust_zones: [public] internal-agent: allowed_labels: [public, internal] trust_zones: [public, internal] path_rules: - id: private-path pattern: private/** labels: [internal] trust_zone: internal ``` Policy modes: - `off`: allow every result and emit allow decisions. - `audit`: keep results but mark decisions that would have been denied. - `enforce`: deny or redact results before they leave the boundary. Denied behavior: - `on_denied: drop` removes denied results. This is the default. - `on_denied: redact` keeps the envelope but replaces text and value. Object labels can come from document frontmatter, policy metadata, or path rules. Supported frontmatter shapes: ```yaml --- labels: [internal] policy: labels: [internal] trust_zone: internal --- ``` Path rules augment frontmatter labels. This lets a repository declare that everything under `private/**` is internal even if an individual document forgets its frontmatter label. ## CLI Check one decision: ```text mkt policy check public-agent query private/doc.md \ --policy examples/policy/local-label-policy.yaml \ --path private/doc.md ``` Filter local FTS search results: ```text mkt search Knowledge \ --policy examples/policy/local-label-policy.yaml \ --subject public-agent ``` Filter indexed query results: ```text mkt cache query 'sections[heading=Decision]' \ --policy examples/policy/local-label-policy.yaml \ --subject public-agent ``` Map NetKingdom/key-cape-style claims into a Markitect policy subject: ```text mkt policy subject examples/policy/netkingdom-claims.yaml \ --policy-map examples/policy/enterprise-policy-map.yaml ``` Inspect a Markitect resource manifest intended for flex-auth registration: ```text mkt policy resource-manifest examples/policy/flex-auth-resource-manifest.yaml ``` JSON and YAML outputs include: - `policy`: mode, subject, action, allowed, denied, redacted, audit counts - `policy_decisions`: per-result decisions with stable ids and reasons - `diagnostics`: denied/redacted result diagnostics Text output shows a compact policy summary before the filtered matches. ## Decision Logs Every local decision contains: - stable `decision_id` - subject - action - object id - effect: `allow`, `deny`, `redact`, or `audit_denied` - reason - mode - rule id - labels - trust zone - metadata, including object path and policy id `LocalLabelPolicyGateway.explain_decision(decision_id)` returns a decision made by the current gateway instance. Persistent decision logs are intentionally left to future backend storage. ## Adapter Boundaries Enterprise IAM integration is covered in `docs/enterprise-access-control-integration.md`. In that architecture, Markitect is the policy enforcement point for Markdown knowledge results, while NetKingdom/key-cape-compatible OIDC supplies identity claims and external policy engines can act as policy decision points. Identity and directory integration use these provider-neutral boundaries: - `IdentityClaimsAdapter` validates OIDC/JWT/SAML material and returns normalized `EnterpriseIdentity`. - `DirectoryGroupResolver` resolves group overage or stale directory claims through SCIM/Graph/LDAP/Keycloak-style adapters. - `EnterprisePolicyMapper` maps canonical enterprise roles, scopes, and groups to `PolicySubject` labels, trust zones, and allowed actions. - `DecisionLogStore` persists durable audit records for policy decisions. The Markitect-side enterprise helpers provide deterministic local implementations: - `NetKingdomIdentityClaimsAdapter` validates required IAM-profile claims, issuer, audience, token lifetime, local-production issuer safety, roles, and scopes for trusted claims or explicit JWT fixtures. - `StaticDirectoryGroupResolver` records group overage/freshness for tests and development. - `EnterprisePolicyMap` and `LocalEnterprisePolicyMapper` translate groups, roles, and scopes into `PolicySubject` labels, trust zones, and actions. - `FlexAuthResourceManifest` describes Markitect knowledge resources that a future flex-auth service can register. - `LocalDecisionLogStore` is a JSONL development sink; durable enterprise audit remains flex-auth scope. Relationship policies use `RelationshipPolicyAdapter`: ```text RelationshipPolicyRequest(subject, relation, object_id, namespace, context) -> PolicyDecision ``` This is the attachment point for Zanzibar/OpenFGA/SpiceDB-style systems. Rule policies use `RulePolicyAdapter`: ```text RulePolicyRequest(subject, action, object, context, policy_id) -> PolicyDecision ``` This is the attachment point for OPA/Rego, Cedar, or other attribute/rule-based systems. Adapters must return the same `PolicyDecision` shape as the local label gateway. That keeps query filtering, diagnostics, provenance, and future context-package filtering independent from the concrete policy engine. ## Extension Fit The local gateway is registered as `policy.local-label`. It is an internal extension with no network dependency. Backends and workflows can request the `policy` or `policy_filter` capability without importing an external service. The design intentionally stays close to Markdown: labels can live in document frontmatter, path rules live in YAML, and external authorization languages are extensions rather than replacements for the core contract.