Files
markitect-tool/docs/access-control-policy-gateway.md

218 lines
6.6 KiB
Markdown

# 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.