Skip to content

engine: enforce raise(queue) ⊆ department M.spec.produces (capability grant, fail-closed) + non-forgeable raised transport #130

@loning

Description

@loning

Upgrade M.spec.produces from a declaration/topology hint into a real per-department capability grant, so the package-repo can make "only the canonical producer emits a marker-gated trigger" UNREPRESENTABLE (capability restriction) instead of only DETECT-able by a conformance scan. This is the engine half of the fkst "PREVENT > DETECT" harness gradient (CLAUDE.md, Harness 本质).

Current state (verified by reading the engine)

  • raise() only namespace-resolves the queue then pushes the buffer — it does NOT check the resolved queue against the calling department's declared M.spec.produces (crates/fkst-framework/src/raise.rs).
  • Production run wires raise with a plain name_resolver(), no declared-produces allowlist (src/main.rs run path).
  • Test mode is stricter (test_runner.rs with_recorded_only_queues / declared_qualified_produces) but that's not a full production raise ⊆ produces enforcement.
  • So any department can raise("any_queue") regardless of its produces declaration → the produces topology is advisory, and "one canonical producer" can only be scanned, not prevented.

Ask

  1. Fail-closed raise ⊆ produces: when a department's pipeline calls raise(queue, …), after namespace resolution the queue MUST be in that department's declared (qualified) M.spec.produces, else error() fail-closed (DLQ/L2). Production AND test same-shape (the test mode already has the pieces). This makes produces the capability grant: a department literally cannot emit a queue it didn't declare.
  2. Non-forgeable raised transport: RAISED: is currently a stdout protocol a sandboxed Lua department could print directly, bypassing raise() + the produces check. Move raised events to a trusted control channel / authenticate the frames so a department cannot forge a RAISED: line — otherwise the capability guard is bypassable. (If already non-forgeable, document why.)

Why

Lets the package-repo declare a marker-gated trigger (e.g. devloop_ready) in ONLY the canonical producer's produces; other departments that don't declare it cannot raise it → the write/read-race bypass class becomes unrepresentable, not just scanned (the forward-direct-raise ratchet in packages then shrinks to 0 by construction). Apply the one-producer rule to lifecycle/authority queues; telemetry/fanout/shared queues stay legitimately multi-producer (the package declares which). Engine-side capability primitive; the package-side adoption + conformance is a separate packages issue (blocked on this).

Reference: object-capability security, make-illegal-states-unrepresentable, narrow-waist. Design via sshx thinking triplet (3 codex read the engine source + converged) + ChatGPT Pro. ⟦AI:FKST⟧

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions