6.6 KiB
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:
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:
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: dropremoves denied results. This is the default.on_denied: redactkeeps the envelope but replaces text and value.
Object labels can come from document frontmatter, policy metadata, or path rules. Supported frontmatter shapes:
---
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:
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:
mkt search Knowledge \
--policy examples/policy/local-label-policy.yaml \
--subject public-agent
Filter indexed query results:
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:
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:
mkt policy resource-manifest examples/policy/flex-auth-resource-manifest.yaml
JSON and YAML outputs include:
policy: mode, subject, action, allowed, denied, redacted, audit countspolicy_decisions: per-result decisions with stable ids and reasonsdiagnostics: 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, oraudit_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:
IdentityClaimsAdaptervalidates OIDC/JWT/SAML material and returns normalizedEnterpriseIdentity.DirectoryGroupResolverresolves group overage or stale directory claims through SCIM/Graph/LDAP/Keycloak-style adapters.EnterprisePolicyMappermaps canonical enterprise roles, scopes, and groups toPolicySubjectlabels, trust zones, and allowed actions.DecisionLogStorepersists durable audit records for policy decisions.
The Markitect-side enterprise helpers provide deterministic local implementations:
NetKingdomIdentityClaimsAdaptervalidates required IAM-profile claims, issuer, audience, token lifetime, local-production issuer safety, roles, and scopes for trusted claims or explicit JWT fixtures.StaticDirectoryGroupResolverrecords group overage/freshness for tests and development.EnterprisePolicyMapandLocalEnterprisePolicyMappertranslate groups, roles, and scopes intoPolicySubjectlabels, trust zones, and actions.FlexAuthResourceManifestdescribes Markitect knowledge resources that a future flex-auth service can register.LocalDecisionLogStoreis a JSONL development sink; durable enterprise audit remains flex-auth scope.
Relationship policies use RelationshipPolicyAdapter:
RelationshipPolicyRequest(subject, relation, object_id, namespace, context)
-> PolicyDecision
This is the attachment point for Zanzibar/OpenFGA/SpiceDB-style systems.
Rule policies use RulePolicyAdapter:
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.