docs: preserve reviewed NetKingdom session artifacts
This commit is contained in:
parent
3add9f8790
commit
b53eed4073
43 changed files with 4653 additions and 0 deletions
|
|
@ -0,0 +1,265 @@
|
|||
diff --git a/canon/schemas/security-case-type_v0.1.schema.json b/canon/schemas/security-case-type_v0.1.schema.json
|
||||
index 22ab31c..3575d8a 100644
|
||||
--- a/canon/schemas/security-case-type_v0.1.schema.json
|
||||
+++ b/canon/schemas/security-case-type_v0.1.schema.json
|
||||
@@ -132,6 +132,24 @@
|
||||
"items": {
|
||||
"type": "object"
|
||||
}
|
||||
+ },
|
||||
+ "execution_descriptors": {
|
||||
+ "type": "object",
|
||||
+ "maxProperties": 128,
|
||||
+ "propertyNames": {
|
||||
+ "type": "string",
|
||||
+ "pattern": "^[a-z0-9][a-z0-9-]*$",
|
||||
+ "not": {
|
||||
+ "pattern": "[^a-z0-9-]"
|
||||
+ }
|
||||
+ },
|
||||
+ "additionalProperties": {
|
||||
+ "type": "string",
|
||||
+ "pattern": "^sha256:[0-9a-f]{64}$",
|
||||
+ "minLength": 71,
|
||||
+ "maxLength": 71
|
||||
+ },
|
||||
+ "description": "Optional exact owner machine-descriptor byte digests keyed by declared follow-through step. A pin alone is not owner acknowledgment, execution authority or native evidence."
|
||||
}
|
||||
}
|
||||
}
|
||||
diff --git a/tools/security-case-types/README.md b/tools/security-case-types/README.md
|
||||
index dc4fc91..d25d2e9 100644
|
||||
--- a/tools/security-case-types/README.md
|
||||
+++ b/tools/security-case-types/README.md
|
||||
@@ -95,3 +95,18 @@ Integration tests invoke actual sibling Mason `prepare` and Warden `Journal.save
|
||||
with metadata fixtures only. They need those owner source checkouts, and existing
|
||||
type suites need the public KeyCape request file and provider declarations beside
|
||||
the checkout. `NET_KINGDOM_ROOT` selects the source root for the 1.2 package tests.
|
||||
+
|
||||
+## Machine descriptor source pins
|
||||
+
|
||||
+The optional `spec.execution_descriptors` map uses the existing Warden kernel's
|
||||
+step-to-exact-descriptor-byte SHA-256 contract. The JSON schema accepts at most
|
||||
+128 lowercase `sha256:<64 hex>` pins. The semantic validator additionally requires
|
||||
+every key to name a declared follow-through step. Malformed maps are refused by
|
||||
+the direct evidence API too. Absent and partial maps remain legal for source
|
||||
+preparation; missing steps remain explicit in the evidence report.
|
||||
+
|
||||
+A valid pin is source metadata. It does not authenticate its owner, acknowledge
|
||||
+a machine seam, admit an adapter or change the implemented-type requirement.
|
||||
+The existing browser types contain no descriptors; their bytes and lifecycle
|
||||
+states are unchanged. Owner descriptor/provenance acceptance and actual native
|
||||
+receipts remain prerequisites to execution and acceptance.
|
||||
diff --git a/tools/security-case-types/security_case_evidence.py b/tools/security-case-types/security_case_evidence.py
|
||||
index 0bf0a84..5f25577 100644
|
||||
--- a/tools/security-case-types/security_case_evidence.py
|
||||
+++ b/tools/security-case-types/security_case_evidence.py
|
||||
@@ -135,6 +135,11 @@ def check_steps(rows, case_source, type_source, rendered):
|
||||
"journal_schema", "evidence_digest", "session_revoked", "session_revocation_digest",
|
||||
"grant_revocation_digest", "attempt_id", "operation"}
|
||||
type_spec = yaml.safe_load(type_source)["spec"]
|
||||
+ # The direct API must refuse malformed maps too, without AttributeError or
|
||||
+ # treating an undeclared step as an admitted descriptor.
|
||||
+ from security_case_validator import validate_execution_descriptors
|
||||
+ if validate_execution_descriptors(type_spec):
|
||||
+ raise EvidenceRefused("invalid type machine descriptor pins")
|
||||
descriptors = type_spec.get("execution_descriptors", {})
|
||||
seen, result = set(), []
|
||||
for row in rows:
|
||||
diff --git a/tools/security-case-types/security_case_validator.py b/tools/security-case-types/security_case_validator.py
|
||||
index 1dc63d6..06cfff4 100644
|
||||
--- a/tools/security-case-types/security_case_validator.py
|
||||
+++ b/tools/security-case-types/security_case_validator.py
|
||||
@@ -151,6 +151,7 @@ def validate_type(data: dict[str, Any], type_dir: Path | None = None) -> list[Is
|
||||
|
||||
params = _validate_type_parameters(spec["parameters"], issues)
|
||||
step_ids = _validate_follow_through(spec["follow_through"], issues)
|
||||
+ issues.extend(validate_execution_descriptors(spec))
|
||||
_validate_composition(spec["composition"], params, issues)
|
||||
_validate_materialization(spec["materialization"], params, issues)
|
||||
_validate_verification(spec["verification"], params, issues)
|
||||
@@ -190,6 +191,30 @@ def validate_type(data: dict[str, Any], type_dir: Path | None = None) -> list[Is
|
||||
return issues
|
||||
|
||||
|
||||
+def validate_execution_descriptors(spec: dict[str, Any]) -> list[Issue]:
|
||||
+ """Validate optional source pins; never infer acknowledgment or authority.
|
||||
+
|
||||
+ Incomplete/absent maps remain legal for plan-only types. Admission and
|
||||
+ provenance remain the executing owner's responsibility.
|
||||
+ """
|
||||
+ if "execution_descriptors" not in spec:
|
||||
+ return []
|
||||
+ path = "type.spec.execution_descriptors"
|
||||
+ pins = spec["execution_descriptors"]
|
||||
+ if type(pins) is not dict or len(pins) > 128:
|
||||
+ return [err(path, "must be a mapping of at most 128 step descriptor pins")]
|
||||
+ steps = spec.get("follow_through")
|
||||
+ declared = {step.get("id") for step in steps
|
||||
+ if type(step) is dict and type(step.get("id")) is str} if type(steps) is list else set()
|
||||
+ issues = []
|
||||
+ for step, pin in pins.items():
|
||||
+ if type(step) is not str or not ID.fullmatch(step) or step not in declared:
|
||||
+ issues.append(err(path, "descriptor key must identify a declared follow-through step"))
|
||||
+ if type(pin) is not str or re.fullmatch(r"sha256:[0-9a-f]{64}", pin) is None:
|
||||
+ issues.append(err(path, "descriptor pin must be a lowercase SHA-256 byte digest"))
|
||||
+ return issues
|
||||
+
|
||||
+
|
||||
def _validate_type_parameters(params: Any, issues: list[Issue]) -> dict[str, dict]:
|
||||
out: dict[str, dict] = {}
|
||||
if not isinstance(params, list) or not params:
|
||||
diff --git a/workplans/NK-WP-0048-security-case-types.md b/workplans/NK-WP-0048-security-case-types.md
|
||||
index 074efd1..72f1a9b 100644
|
||||
--- a/workplans/NK-WP-0048-security-case-types.md
|
||||
+++ b/workplans/NK-WP-0048-security-case-types.md
|
||||
@@ -9,7 +9,7 @@ flavor: implementation
|
||||
owner: claude-code
|
||||
topic_slug: netkingdom
|
||||
created: "2026-10-08"
|
||||
-updated: "2026-10-10"
|
||||
+updated: "2026-10-16"
|
||||
depends_on: []
|
||||
related_repos:
|
||||
- ops-mason
|
||||
@@ -323,7 +323,7 @@ the rationale. The type is `acknowledged`; it becomes `implemented` with T06.
|
||||
```task
|
||||
id: NK-WP-0048-T06
|
||||
status: wait
|
||||
-depends_on: [NK-WP-0048-T05, NK-WP-0048-T08, NK-WP-0048-T09, MASON-WP-0010-T03, WARDEN-WP-0044-T03]
|
||||
+depends_on: [NK-WP-0048-T05, NK-WP-0048-T08, NK-WP-0048-T09, NK-WP-0048-T12, MASON-WP-0010-T03, WARDEN-WP-0044-T03]
|
||||
blocking_reason: "Type1.2 architecture has a confirmed source-only founder acknowledgment; canonical1.1 remains unchanged/default and the1.2 reference case stays proposed. Needs explicit version selection/reference-source integration without normal admission bypass, closed owner adapters plus global/native receipt verifier (WARDEN-WP-0044-T03), independently verified six-object structural materialization and CCR42 claims/created-only provenance, exact broker/case/source reconciliation, then separately authorized native mint/delivery and attended registry/consumer verification. Source acknowledgment, a bundle or local tests do not prove implemented/materialized/live status."
|
||||
priority: high
|
||||
state_hub_task_id: "d9b26bcf-a7c6-59a2-92a0-f913d76a203c"
|
||||
@@ -584,3 +584,61 @@ validator, then project only descriptor-declared safe target metadata; do not
|
||||
remove the credential/value refusal or copy complete parameters into journals.
|
||||
Actual owner descriptor acknowledgment and independent evidence admission remain
|
||||
prerequisites, with normal admission still refusing unimplemented types.
|
||||
+
|
||||
+
|
||||
+## T10 — Source compatibility checker for Mason and Warden evidence
|
||||
+
|
||||
+```task
|
||||
+id: NK-WP-0048-T10
|
||||
+status: done
|
||||
+priority: high
|
||||
+```
|
||||
+
|
||||
+Completed 2026-10-16 in published source `c31d21ba`. The read-only
|
||||
+`check-evidence` command checks the actual Mason structural request and Warden
|
||||
+journal source protocols against an exact version/render. Independent source
|
||||
+review passed 103 tests at that checkpoint. It always reports native completion
|
||||
+false and execution prohibited. Fixtures and local journal fingerprints do not
|
||||
+provide authenticated owner/native evidence. This records completed source scope
|
||||
+previously described under T06; it does not duplicate native acceptance credit.
|
||||
+
|
||||
+## T11 — Validate the optional machine-descriptor pin contract
|
||||
+
|
||||
+```task
|
||||
+id: NK-WP-0048-T11
|
||||
+status: done
|
||||
+priority: high
|
||||
+```
|
||||
+
|
||||
+Completed 2026-10-16. Reproduced the JSON schema rejecting a valid
|
||||
+`execution_descriptors` map while the Python validator accepted malformed maps.
|
||||
+The schema now supports the existing Warden optional exact-byte pin map; semantic
|
||||
+validation binds keys to declared steps, bounds the map and checks digest shape.
|
||||
+The direct evidence API refuses malformed maps before accessing them. Twenty-one
|
||||
+new checks and all 127 affected source checks passed. No current type/case bytes,
|
||||
+status, owner descriptors or native acceptance changed. Source evidence and
|
||||
+publication receipt: Custodian
|
||||
+`docs/changes/netkingdom-target-2026-10-16/browser-acceptance-next/`.
|
||||
+
|
||||
+## T12 — Prepare NetKingdom-owned attended step adapters
|
||||
+
|
||||
+```task
|
||||
+id: NK-WP-0048-T12
|
||||
+status: todo
|
||||
+priority: high
|
||||
+```
|
||||
+
|
||||
+Owner: NetKingdom. Next action: wrap the existing contained `keycape-client.py`
|
||||
+registration/config/env and identity-provisioner ensure/revoke entry points in
|
||||
+bounded owner adapters or receipt-only attended steps for Warden's closed kernel.
|
||||
+Reuse the existing caller/session boundary, CAS/readback and status-only outputs;
|
||||
+never acquire user-engine's runtime identity or expose registry/client secrets.
|
||||
+Completion requires exact adapter/source descriptors, positive and refusal source
|
||||
+checks, uncertainty/created-only rollback behavior and a separate owner review of
|
||||
+the proposed machine seam. Source preparation can begin without credentials.
|
||||
+Warden independently owns global provenance/admission integration; Platform owns
|
||||
+mint/broker and native structural receipts; Apps owns consumer delivery/group
|
||||
+acceptance. Their assent is not supplied by this task. Pinning descriptors into
|
||||
+an acknowledged type remains a separately reviewed type-source change. Actual
|
||||
+attended execution still requires its existing owner authorization and native
|
||||
+receipts. T06 retains all native/reference lifecycle acceptance scope.
|
||||
diff --git atools/security-case-types/tests/test_descriptor_pins.py btools/security-case-types/tests/test_descriptor_pins.py
|
||||
new file mode 100644
|
||||
index 0000000..8980425
|
||||
--- /dev/null
|
||||
+++ btools/security-case-types/tests/test_descriptor_pins.py
|
||||
@@ -0,0 +1,64 @@
|
||||
+"""Owner pin contract parity; synthetic pins never establish native acceptance."""
|
||||
+import copy
|
||||
+import json
|
||||
+from pathlib import Path
|
||||
+import sys
|
||||
+
|
||||
+import jsonschema
|
||||
+import pytest
|
||||
+import yaml
|
||||
+
|
||||
+TOOL = Path(__file__).resolve().parents[1]
|
||||
+REPO = TOOL.parents[1]
|
||||
+sys.path.insert(0, str(TOOL))
|
||||
+import security_case_validator as v
|
||||
+import security_case_evidence as e
|
||||
+
|
||||
+TYPE = yaml.safe_load((REPO / 'security-case-types/_template/type.yaml').read_bytes())
|
||||
+SCHEMA = json.loads((REPO / 'canon/schemas/security-case-type_v0.1.schema.json').read_bytes())
|
||||
+PIN = 'sha256:' + 'a' * 64
|
||||
+STEP = TYPE['spec']['follow_through'][0]['id']
|
||||
+
|
||||
+
|
||||
+def with_pins(pins):
|
||||
+ doc = copy.deepcopy(TYPE)
|
||||
+ doc['spec']['execution_descriptors'] = pins
|
||||
+ return doc
|
||||
+
|
||||
+
|
||||
+@pytest.mark.parametrize('pins', [{}, {STEP: PIN}])
|
||||
+def test_optional_partial_pin_contract_has_schema_semantic_parity(pins):
|
||||
+ doc = with_pins(pins)
|
||||
+ jsonschema.validate(doc, SCHEMA)
|
||||
+ assert not v.has_errors(v.validate_type(doc))
|
||||
+ assert doc['spec']['status'] == TYPE['spec']['status']
|
||||
+
|
||||
+
|
||||
+@pytest.mark.parametrize('pins', [None, [], 'digest', {STEP: True}, {STEP: None},
|
||||
+ {STEP: 17}, {STEP: 'a' * 64}, {STEP: PIN.upper()}, {STEP: PIN + '\n'},
|
||||
+ {STEP + '\n': PIN}, {7: PIN}, {'../escape': PIN},
|
||||
+ {f'step-{i}': PIN for i in range(129)}])
|
||||
+def test_malformed_descriptor_contract_refused_by_both_validators(pins):
|
||||
+ doc = with_pins(pins)
|
||||
+ assert v.has_errors(v.validate_type(doc))
|
||||
+ with pytest.raises(jsonschema.ValidationError):
|
||||
+ jsonschema.validate(doc, SCHEMA)
|
||||
+
|
||||
+
|
||||
+def test_well_shaped_unknown_step_is_semantic_refusal():
|
||||
+ doc = with_pins({'not-a-declared-step': PIN})
|
||||
+ jsonschema.validate(doc, SCHEMA)
|
||||
+ assert v.has_errors(v.validate_type(doc))
|
||||
+
|
||||
+
|
||||
+@pytest.mark.parametrize('pins', [None, [], {STEP: 'bad'}, {'undeclared-step': PIN}])
|
||||
+def test_direct_evidence_api_refuses_bad_map_without_crashing_or_needing_rows(pins):
|
||||
+ with pytest.raises(e.EvidenceRefused, match='machine descriptor pins'):
|
||||
+ e.check_steps((), b'case', yaml.safe_dump(with_pins(pins)).encode(), {'follow_through': []})
|
||||
+
|
||||
+
|
||||
+def test_partial_pin_cannot_bypass_ordinary_case_admission():
|
||||
+ doc = with_pins({STEP: PIN})
|
||||
+ case = yaml.safe_load((REPO / 'security-case-types/_template/examples/example-operator.yaml').read_bytes())
|
||||
+ issues = v.validate_case(case, doc)
|
||||
+ assert any('only implemented types' in issue.message for issue in issues)
|
||||
Loading…
Add table
Add a link
Reference in a new issue