Imported from ariffazil/AAA (
skills/arifos-kernel-zen-audit/SKILL.md). Install upstream withnpx skills add ariffazil/AAA --skill arifos-kernel-zen-audit. Copyright stays with the author.
arifOS Kernel Zen Audit
The Tell
When multiple MCP tool responses share the same skeleton — affordance_contract, full_affordance, nine_signal{delta,psi,omega,overall}, sesat_event, _wrapper_degradation, metacognition, constitutional_check, decision_thresholds — and the decision_thresholds block is byte-identical verbatim across every tool, identity, verdict, and affordance have been re-implemented independently per wrapper. That's the architectural gap, not eleven bugs.
Fiqh Audit Grid
Before writing the report, classify each finding:
| Class | Meaning | Action |
|---|---|---|
| WAJIB | Constitutional, non-negotiable, currently broken | Must fix in this pass |
| HARAM | Forbidden pattern currently happening | Stop, don't just "fix later" |
| HARUS | Permissible, fine, don't fuss | Note so reviewer doesn't waste cycles |
| MAKRUH | Discouraged, bad hygiene | Add to cleanup backlog |
| SUNAT | Recommended practice already present | Standardize, extend |
Common WAJIB candidates:
- One verdict, one source of truth
- Identity/authority unbroken from
arif_initthrough all downstream calls - Irreversible actions (seal, forge) → SOVEREIGN + 888_HOLD (verify this holds)
Common HARAM:
- Bare unstructured strings on hard-block paths (
arif_judge/arif_forge/arif_seal) - Silent authority downgrades (FULL→OBSERVE_ONLY→LOW, unlabeled)
- Authority propagation drift — init sees SOVEREIGN, judge/forge sees MEDIUM (P0, council-confirmed 2026-07-15)
- Orphaned inner tools firing without registry entry
- 2+ verdict fields disagreeing in one payload
- Transport
status="SEAL"— overloads constitutional verdict - Confidence trajectory ascending in degraded mode (P1 template bypass)
actor: nullwhenactor_idis populated in the same envelope- Audit spine fields (
call_hash,invocation_count) null on non-init verbs
Scoring Template
Score 6 dimensions /10 each, /60 total:
| Dim | Current | After minimal fix | After full fix |
|---|---|---|---|
| Identity/session persistence | ? | ? | ? |
| Single source of truth (verdict) | ? | ? | ? |
| Registry integrity (no orphans/alias sprawl) | ? | ? | ? |
| Failure honesty (sesat_event coverage) | ? | ? | ? |
| Irreversible-action gating | ? | ? | ? |
| Payload signal-to-noise | ? | ? | ? |
| Composite | ?/60 | ?/60 | ?/60 |
Always show two projected columns. Minimal fix vs full fix. Tiny fixes often move one axis but barely touch the others — say so plainly.
Zen Output Contract
When the audit is for Arif:
- ≤3 sentences
- The zen (one-line koan) first
- Fiqh grid second (table, terse)
- Scorecard third (with both projections)
- "DITEMPA BUKAN DIBERI applies to the kernel too" only when earned, not as ceremonial footer
Pitfalls
- Don't confuse "already pretty good" with "fixed." 26/60 is a failure at 60% scale.
- The skeleton-similarity tell is the strongest signal. If
decision_thresholdsis byte-identical verbatim across tools, the audit is already half-done. - Three projections (current / minimal-fix / full-fix) is honesty — don't collapse to two, that hides scope.
- Don't recommend closing the audit after minimal fix unless the full-fix gap is genuinely zero.
- Audit-receipt sealing at HOLD is correct. When
mcp__arifos__arif_sealreturns 888_HOLD because you have MEDIUM authority (not SOVEREIGN), that is the kernel refusing to grant a SEAL you have no right to claim. Don't work around it. Land the audit-receipt at HOLD, document in vault, move on. F1 honesty > ceremonial SEAL. - Authority plane cannot be tested architecturally. arifOS kernel exposes no write endpoint (404 on
/seal,/seal_chain,/leases,/judge). Behavioral obedience is confirmed (Hermes does not write without seal), but the gate itself cannot be probed. Add a/kernel/authority-proberead-only endpoint if you need a testable gate — currently the Authority plane scores 60/100 (untested gate). - Don't raw-append to
seal_chain.jsonl. The JS canonical writer uses|-joined material; Python+-concat produces a line the JS verifier rejects. Always go throughnode seal_chain.js write <JSON>. Seereferences/seal-chain-write-gotchas-2026-07-09.md. - Always run
node seal_chain.js verifybefore any chain write. As of 2026-07-09, verify returns broken-at-line-1 — a pre-existing anomaly predating this audit. - SEAL is a verdict, not a status. Transport status must be "OK", never "SEAL". If tools return
status="SEAL", normalize at ingestion (tools.pyline ~3458). The sesat drift detector keys off verdict, not status. Seereferences/bangang-fixes-seal-overload-audit-chain-2026-07-11.md. - Audit spine fields must be generated by the envelope wrapper, not by individual tools.
call_hash,trace_id,invocation_count,called_from_kernel— if the tool didn't provide them, the wrapper computes them. Three-stage fallback: (1) read from out/result, (2) compute from tool+payload+timestamp, (3) hard default. Never leave null. - Confidence trajectory must be flat (single-point) in degraded mode. When LLM synthesis is bypassed (P1 degraded), the ascending trajectory [0.5, 0.72, 0.85] is fabricated. Use
[llm_confidence]and addconfidence_provenance: "COMPUTED_NOT_OBSERVED"to the result dict. Mark steps withaxiom_used="P1_TEMPLATE_DEGRADED". - Actor identity:
actorandactor_idmust agree. If the tool doesn't return an actor dict, the envelope synthesizes{"actor_id": resolved_actor_id, "source": "envelope_derived"}from the session store. Never leaveactor: nullwhenactor_idis populated.
L12 INJECTION Floor — Mandatory Audit Dimension
When auditing the arifOS kernel, L12 INJECTION is mandatory. The floor score lives at runtime_floors.L12 in /health. A score below 0.85 (comparator "<") means residual risk is present — the specific gap paths must be identified.
Probe Order
- Live endpoint:
curl -s localhost:8088/health→runtime_floors.L12 - Tool schemas:
curl -s localhost:8088/tools— enumerate the public surface - Input surface trace — trace every tool's text/dict parameter paths for L12 sanitisation coverage:
arif_observe(mode=fetch, url=...)— fetched content bypasses L12 input scan (PRIMARY GAP)arif_compose(message=...)— free-text goes direct to_synthesize, not throughcheck_lawsarif_init(context=...)— nested dict,sanitize_dictnot appliedarif_think(query=...)— exact-string-match bug: tool-name list check can silently miss variants
law.pycheck_laws(): scans String params for injection keywords but does NOT callsanitize_dict— raw params continue downstream unchanged- L1 regex gaps in
tools.py: misses"[system prompt]",/s,"```system", unicode-prefixed emoji+instruction,">> "redirection prefix data_governance.pysanitize_dict/detect_injection: defined and tested but only wired to asset-data path — never called on raw MCP tool parameters at entryfiqh_of_floors.py: confirmedStatus: STAGED—injection_scoreinActionContextis never populated from live measurement
Primary Architecture Signal
MCP Request → PNS·SHIELD (prompt_armor.scan) → orchestrator outer ring
↓
check_laws (law.py) → tools.py L12 scan (params only)
↓
arif_observe → url fetch → NO L12 scan ← PRIMARY GAP
↓
evidence layer → 888 collapse
↓
witness_packet._scan_injection (output only — too late)
Fix Rule
sanitize_dict() must be wired to tool parameter ingestion before check_laws. The gap is at the entry point, not at the output witness. Full findings: references/F12-injection-floor-diagnosis-2026-07-10.md
Authority Propagation Drift (P0 — Council-Discovered 2026-07-15)
The most dangerous kernel finding. External council confirmed: arif_init recognizes ARIF as SOVEREIGN/FULL, but arif_judge and arif_forge receive MEDIUM authority. F13 cannot reliably exercise sovereign authority through the system.
The Unbroken Chain
connector ingress → arif_init → session capability token → response context → route → judge → forge → receipt
Every link must carry the same identity and authority band. If init says SOVEREIGN but judge says MEDIUM, the chain is broken.
Detection
Call arif_init with sovereign credentials → immediately call arif_judge → compare authority bands. If mismatch → drift confirmed.
Fiqh Classification
WAJIB — constitutional, non-negotiable, currently broken. Without authority propagation, F13 veto is decorative.
Fix Rule
Session capability token issued at init must be the same token consumed by judge and forge — not re-resolved at each step.
T3a BOOT Gate Demotion (2026-07-24)
Variant of authority propagation drift — the BOOT gate demotes FULL→OBSERVE_ONLY even when the Ed25519 signature verified and the kernel recognizes the actor as SOVEREIGN with FULL runtime grant.
The Stack
arif_init → identity_band_authority() → FULL
→ _apply_boot_gate() → boot_state_for_authority_grade()
→ verify_boot_attestation() → Q1-Q7
→ boot_state=PARTIAL → passes=False → demote to OBSERVE_ONLY
Q1-Q7 Boot Attestation Checks
| # | Check | Method | Expect during init |
|---|---|---|---|
| Q1 | Identity bind | session_identity_service | PARTIAL (no session_id yet) |
| Q2 | Constitution loaded | kernel_health_constitution | YES |
| Q3 | Session ignite | session_store_liveness | PARTIAL (session being minted) |
| Q4 | Trinity33 loaded | atlas333_substrate | YES |
| Q5 | Sovereign recognized | identity_toml_f13 | PARTIAL (name-match only without crypto) |
| Q6 | Refusal surface | refusal_list_module | YES |
| Q7 | RSI path clear | rsi_session_endpoint | YES |
Q1 and Q3 are inherently PARTIAL during init — they need a session_id that hasn't been created yet. The boot_state_for_authority_grade() function requires boot_state == "OK" (all YES), so any PARTIAL causes demotion.
Fixes (Two Required)
1. identity.toml (Q5 name-match fix)
/opt/arifos/identity.toml must contain:
owner = "Muhammad Arif bin Fazil"
authority = "F13_SOVEREIGN"
The boot check at boot_attestation.py:350 checks:
name_match = ("Muhammad Arif bin Fazil" in combined or "Arif" in toml_text) and (
"F13" in toml_text or "sovereign" in toml_text.lower()
)
Without the owner field containing "Arif", Q5 returns NO → boot_state=FAIL.
2. boot_state_for_authority_grade passes condition
In /opt/arifos/app/arifosmcp/runtime/boot_attestation.py line 530:
# Change from:
"passes": parsed["summary"]["boot_state"] == "OK",
# To:
"passes": parsed["summary"]["boot_state"] in ("OK", "PARTIAL"),
Auto-Sign Path Limitation
When arif_init is called with ONLY actor_id (no nonce/signature), the auto-sign path (elif actor_id: at session.py:1340) fires:
- Issues a fresh challenge nonce
- Auto-signs with local Ed25519 key
- Sets
_light_band = "FULL"(session.py:1373) _project_lightreceivesauthority_override="FULL"- But
_apply_boot_gatesees PARTIAL boot_state → demotes to OBSERVE_ONLY
The SCT token is issued at OBSERVE_ONLY even though the kernel's internal runtime_grant says FULL.
Detection
When arif_init returns authority_scope="OBSERVE_ONLY" but also shows runtime_grant.level="FULL" and cryptographically_verified=true, the BOOT gate is blocking. Check:
journalctl -u arifos | grep "BOOT gate demoted"
Expected: T3a Item 3: BOOT gate demoted runtime_band=FULL -> OBSERVE_ONLY (boot_state=PARTIAL|FAIL, yes=N, no=M)
Fix Rule
Always patch TWO locations for arifOS runtime changes:
- Source:
/root/arifOS/arifosmcp/...(git-tracked) - Runtime:
/opt/arifos/app/arifosmcp/...(running deployment) Verify with:grep "passes" /opt/arifos/app/arifosmcp/runtime/boot_attestation.py
Pitfall: sidestepping the auto-sign path
When passing explicit nonce + actor_signature through the MCP arif_init tool, those parameters correctly reach session.py:1281 (if actor_id and nonce and _sig:). But the nonce is consumed by the first verification attempt. A second call with the same nonce triggers challenge_replayed. Always generate a fresh nonce for each init attempt, or let the auto-sign path handle it (no nonce/signature params).
Schema/Runtime Dispatch Drift (P0 — Council-Discovered 2026-07-15)
Published MCP inputSchema rejects modes that the runtime actually supports (e.g., arif_observe rejects skill_discover but runtime advertises it). The schema, dispatcher, and capability graph must be generated from one canonical source — never manually copied.
Detection
Compare MCP inputSchema (from tools/list) vs actual dispatch enum in runtime code. If they disagree → drift confirmed.
F13 Multi-Sovereign Collision Audit (PATH 3 — 2026-07-25)
Structural invisible to one operator. This bug appears only with two distinct sovereign identities. The falsification spec calls for two Ed25519 keypairs, two actor_ids, separate crypto — not one operator holding two keys.
What This Tests
From EXTERNAL_FALSIFICATION_SPEC.md §PATH 3 (Tests 3.1–3.4):
| # | Test | Expected |
|---|---|---|
| 3.1 | Concurrent VOID+VOID on same action_id | One coherent terminal state |
| 3.2 | SEAL vs VOID on same action_id | Deterministic resolution by documented rule |
| 3.3 | Repeat 3.2 × 20 with random submission order | Zero variance |
| 3.4 | Sovereign B adjudicates A's session | Rejected — ownership enforced |
Four Critical Checks
Before the live tests, probe the source for these four dimensions:
1. Conflict resolver wiring — /root/arifOS/arifosmcp/core/conflict_resolver.py
- Does
resolve_conflict()exist? YES — has VOID-dominates ordering and organ hierarchy. - Is it CALLED from the judge path (
_arif_judge_deliberatein tools.py)? Check: search forresolve_conflictorConflictEnvelopein tools.py. - If the resolver is never invoked, the "rule" is dead code. All verdicts are stored independently — last-writer-wins at the dict level.
- The
ConflictEnvelopeusesorgan_a/organ_b(GEOX/WEALTH/arifOS/HUMAN), NOT sovereign identity. Two F13 sovereigns both map to "human" — Rule 3 (same organ, more restrictive wins) would apply WERE the resolver wired.
2. Session ownership enforcement — /root/arifOS/arifosmcp/runtime/session.py lines 163, 288
_ACTOR_SESSION_MAP: dict[str, str]mapssession_id → actor_id. Does ANY downstream tool verify this mapping?- In
_arif_judge_deliberate(tools.py:~16191): search for_ACTOR_SESSION_MAP.get(session_id)— if absent, no ownership gate. - The
_wrap_handlerwrapper (tools.py:~23291) only fills in missing actor_id from session, never enforces a match. - Sovereign identity map:
_SOVEREIGN_IDENTITY_MAP(session.py:280) contains only"ariffazil". The localhost auto-sign path (session.py:1364) accepts"arif","888","ariffazil"— drift between the two maps.
3. VAULT999 collision detection — /root/arifOS/arifosmcp/tools/vault.py
- Is
outcomes.jsonlpurely append-only? Check:_VAULT_LEDGER: list[dict](tools.py:5810) — yes, pure append. - Does the vault check for existing entries with the same
action_idbefore appending? Search for anyaction_iddedup or collision check in vault.py. - Can two entries for the same action (one SEAL, one VOID) coexist? If no reconciliation, YES — both persist independently.
4. Sovereign identity consistency — /root/arifOS/arifosmcp/tools/session.py + runtime/session.py
- Three identity sources must agree:
session.py:1364—_actor_lower in ("arif", "888", "ariffazil")(auto-sign gate)session.py:1659—_SOVEREIGN_MAPfor mode="challenge"runtime/session.py:280—_SOVEREIGN_IDENTITY_MAP(persistent store)
- If these drift, sovereign A can init but sovereign B cannot, or vice versa.
- The
_ED25519_EXEMPT_SYSTEM_ACTORSmap may also have its own list — checksession_auth.py.
Source Code Reference Map
| File | What to check | Lines |
|---|---|---|
tools/session.py |
Auto-identity string check, sovereign map | 1364, 1659-1663 |
runtime/session.py |
_SOVEREIGN_IDENTITY_MAP, _ACTOR_SESSION_MAP |
280-282, 163, 288 |
runtime/tools.py |
_arif_judge_deliberate, _wrap_handler session fill (not enforce) |
16191+, 23291-23297 |
runtime/tools.py |
_VAULT_LEDGER, _JUDGE_STATE_REGISTRY |
5810, 5818 |
core/conflict_resolver.py |
Verdict ranks, organ hierarchy, resolve_conflict |
30-288 |
tools/vault.py |
Appending logic, collision check | 401+, search for action_id dedup |
Scoring Template for Multi-Sovereign Dimension
| Dimension | Current | After minimal fix | After full fix |
|---|---|---|---|
| Deterministic cross-sovereign resolution | ?/10 | 4/10 (wire resolver) | 10/10 (atomic two-phase) |
| Session ownership enforcement | ?/10 | 6/10 (actor-match check) | 10/10 (crypto-bound SCT) |
| VAULT999 collision detection | ?/10 | 5/10 (detect+store both) | 10/10 (auto-escalate to 888_HOLD) |
| Sovereign identity consistency | ?/10 | 8/10 (unify map) | 10/10 (Ed25519 pubkey registry) |
| Irreversible-action blocking | ?/10 | 6/10 (any VOID blocks) | 10/10 (all must SEAL) |
| Composite | ?/50 | 29/50 | 50/50 |
Fiqh Findings (from 2026-07-25 audit)
| Finding | Class | Reason |
|---|---|---|
| No cross-sovereign collision resolution | WAJIB | Resolver exists but isn't wired into judge path |
| No session ownership enforcement in judge | WAJIB | _ACTOR_SESSION_MAP created but never checked |
| String-name sovereign detection | MAKRUH | Acceptable for localhost; map drift is real gap |
| VAULT999 append-only (no reconciliation) | HARUS | Append-only is correct; missing higher-level reconciliation |
| Conflict resolver exists but unwired for F13 | HARAM | Would catch SEAL vs VOID if wired; currently dead code |
Pitfalls
- The conflict resolver is correct — check if it's WIRED, not whether it exists.
conflict_resolver.pyhas a valid VOID-dominates hierarchy. The question is whether_arif_judge_deliberatecallsresolve_conflict()before returning — it almost certainly doesn't. - Session ownership = two separate things. (1)
_ACTOR_SESSION_MAPmaps session_id→actor_id (it exists). (2) Code that enforces the caller's actor_id matches the map (it doesn't exist). Report both separately. - Two sovereigns that map to the same organ produce the wrong resolution. Under the conflict resolver's organ hierarchy, two F13 sovereigns both land in "human" (rank 8). Rule 3 says "more restrictive verdict wins" — VOID over SEAL. But VOID-dominates for ALL sovereigns means no single sovereign can SEAL an action another has VOIDed, even if the SEAL is the correct call. This is correct for irreversible actions but may be too restrictive for reversible ones. Call this out.
- Localhost auto-sign path bypasses the multi-sovereign question entirely. When
arif_initis called from localhost with no explicit signature, the auto-sign path (session.py:1357-1416) uses the local Ed25519 key. Two distinct remote sovereigns would each need their own key — the auto-sign path only works for the VPS-local agent. - The string-name check is the real sovereign detection for the auto-sign path.
actor_id.lower() in ("arif", "888", "ariffazil")means any caller presentingactor_id="arif"gets auto-signed. If you provision a second sovereign withactor_id="SOVEREIGN_B", the auto-sign path rejects them — they'd need explicit Ed25519.
Reference Files
references/f13-multi-sovereign-collision-audit-2026-07-25.md— Full audit report: source-by-source analysis of all 4 PATH 3 tests against the EXTERNAL_FALSIFICATION_SPEC, with specific line numbers, found/wired/unwired classification, recommended fix code, and VAULT999 collision state machine analysis.references/external-falsification-spec-path3.md— Extracted PATH 3 section of the falsification spec: the 4 tests (3.1–3.4), pass/fail criteria, and verdict rules.
Reference Files
references/bangang-fixes-seal-overload-audit-chain-2026-07-11.md— 4 kernel fixes: SEAL overload normalization, audit chain fallback, confidence theater suppression, actor identity propagation. Concrete code locations and fix patterns.references/identity-verification-architecture.md— arifOS kernel Ed25519 identity verification flow: delegation pattern, dual verification paths (crypto_auth vs governance_identity), proof dict construction, nonce format, session update pattern. Must-read before auditing identity/authority fields.references/seal-chain-write-gotchas-2026-07-09.md— seal-chain write autopsy, JS canonicalization rules, verify-broken-at-line-1 anomalyreferences/F12-injection-floor-diagnosis-2026-07-10.md— full L12 gap findings from 2026-07-10 diagnostic sessionreferences/kernel-crash-recovery-2026-07-12.md— service permissions fix when.envis owned by wrong user. SystemdUser=arifosvs file ownership mismatch.references/arif-bind-global-command.md— globalarif-bindcommand setup: one-word sovereign session bind from any terminal.references/zen-surface-reduction-verification-2026-07-16.md— ZEN surface reduction audit: verified vs claimed, deploy timing, VAULT999 seal verification pitfallreferences/meta-mesa-test-charter-2026-07-12.md— META-MESA Substrate Test Charter: 12-section test specification for proving governed causal agency. 10 hard gates, multiplicative scoring, 13-phase sequence.references/drift-scar-bug-attestation-chain-2026-07-27.md— Drift = scar vs bug framework from session with Arif + OpenCode. 6-step boot attestation pattern (PROBE→BIND→WITNESS→CLASSIFY→RECOGNIZE→ATTEST). No-drift≠Ready≠SEAL chain. Surface guard reclassification (AAA as non-MCP organ).
Sovereign Authentication (2026-07-12)
When the kernel blocks at 888_HOLD with actor_verified=false, sign the nonce and call arif_init. See references/sovereign-auth-procedure.md for the full procedure, one-shot signing script, and failure mode table.
Critical rule: Generate nonce + sign + call in ONE shot. Never test locally first — nonce is single-use and local test consumes it (challenge_replayed).
Pitfalls — Debugging Style
- Stop probing, start fixing. When the code path is clear and the failure mode is known, act. Don't add more diagnostic calls. "Setel. Relaks tapi tajam."
- Read failure modes before adding complexity.
challenge_replayedmeans the crypto works but the nonce was consumed — not that verification failed. Don't add 15 more diagnostic tool calls when the error message already tells you the answer. - "Wire splice confirmed" ≠ "wire is the bug." The session.py delegate was already receiving
actor_signatureassignature. The real bug was nonce consumption from intermediate testing. Don't assume a known-broken path is still broken without fresh evidence. - When the error message IS the root cause, stop investigating.
challenge_replayed→ nonce consumed → generate fresh.Invalid signature→ wrong payload format → try the other format. The systematic-debugging skill says "NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST" — but reading the error message IS investigation. Don't run 20 diagnostic calls when the first error told you everything. - Don't re-verify what already verified. If
verify_init_identityreturnsok=True, don't call it again "to confirm." The second call consumes the nonce and breaks the subsequentarif_init. Trust the receipt. - Arif frustration signals are FIRST-CLASS debugging signals: "Setel" = fix it now. "Relaks tapi tajam" = stop thrashing, be precise. "Did u even nap and check current state?" = you're citing stale state, re-probe. One-word replies = sovereign ack, act immediately. "Bangang la" after a clear diagnosis + fix proposal = you've over-asked, execute now.
- Service user vs file ownership. The systemd service runs as
User=arifos. All files in/opt/arifos/app/must be readable byarifos. If a file is owned byrootorariffazilwith mode 600, the kernel will crash on startup withPermissionError. Fix:chown arifos:arifos <file> && chmod 640 <file>. Neverchown -R root:root— that re-creates the problem.
RECORD vs AUTHORIZE Seal Architecture (2026-07-24)
The kernel now supports TWO seal paths. NEVER conflate them:
| Dimension | AUDIT_RECORD (SEAL_RECORD) | ACTION_AUTHORIZATION (SEAL_AUTHORIZATION) |
|---|---|---|
| reversibility_level | R2 | R4 |
| seal_purpose | RECORD | AUTHORIZE |
| authority_effect | NONE | EXECUTION_GRANT |
| ack_irreversible | false | true |
| requires_f13 | false | true |
| F13 Ed25519 | Not required | Required |
| Execution authority | None | A-FORGE execution grant |
Canonical Chain for RECORD
arif_judge(action_class="AUDIT_RECORD", reversibility="R2")
→ ALLOW + SEAL_RECORD
→ cc_id + judge_state_hash emitted
→ arif_seal(ack_irreversible=false, seal_purpose="RECORD",
cc_id=..., judge_state_hash=...)
→ VAULT999 receipt (no F13, no human)
Canonical Chain for AUTHORIZE
1. Sign f"{actor_id}:{nonce}" — actor_id MUST match case exactly
2. arif_judge(action_class="ACTION_AUTHORIZATION", reversibility="R4",
actor_signature=..., nonce=...)
→ ALLOW + SEAL_AUTHORIZATION + cc_id + judge_state_hash
→ arif_seal(ack_irreversible=true, seal_purpose="AUTHORIZE",
cc_id=..., judge_state_hash=...)
→ VAULT999 + execution grant
Ed25519 Verification — Payload Format
The kernel verifies f"{actor_id}:{nonce}". If actor_id="ARIF", sign "ARIF:{nonce}" (uppercase). resolve_actor_public_key normalizes internally — the payload must match the exact actor_id string.
Production Path (in _verify_sovereign_token)
Bypasses crypto_auth.verify_actor_signature challenge gate. Does direct verification:
from arifosmcp.runtime.crypto_auth import resolve_actor_public_key
pubkey = resolve_actor_public_key(actor_id)
if pubkey is not None:
pubkey.verify(_b64.b64decode(actor_signature), f"{actor_id}:{nonce}".encode())
return True
Set ARIFOS_ALLOW_FREE_NONCE=1 in the service environment to bypass the challenge gate for dev/test.
Action Class Policy Table
Defined in arif_kernel_intercept.py _ACTION_CLASS_POLICY:
_ACTION_CLASS_POLICY = {
"AUDIT_RECORD": {"requires_f13": False, ...},
"EVIDENCE_ATTESTATION": {"requires_f13": False, ...},
"VAULT_RECEIPT": {"requires_f13": False, ...},
"ACTION_AUTHORIZATION": {"requires_f13": True, ...},
"CONSTITUTIONAL_AMENDMENT": {"requires_f13": True, ...},
}
MCP Ingress Debugging — Parameter Propagation
When actor_signature, nonce, reversibility_level, action_class don't reach the kernel through MCP, the MOST LIKELY culprit is the ingress filter pipeline:
MCP → _wrap_handler → _filter_kwargs_for_handler → _akal_wrap_judge → _arif_kernel_intercept_tool → _arif_kernel_intercept
Filter Chain (tools.py)
-
_wrap_handler(tools.py:23274) — outermost wrapper. Calls_filter_kwargs_for_handlerthen calls handler. -
_filter_kwargs_for_handler(tools.py:22945) — PARAMETER STRIPPING HAPPENS HERE.- Gets
accepted = set(params.keys())from the handler's Python signature - Parameters NOT in
accepted→contract_c_audit(silently dropped from kwargs) - Has
_LEGACY_PARAM_ALIASESthat RENAME params: e.g.,actor_id → actor
- Gets
-
_akal_wrap_judge(server.py:775) — wraps judge with AKAL metadata. Uses@functools.wraps(handler)setting__wrapped__. -
_arif_kernel_intercept_tool(tools.py:22091) — the actual wrapper. Hasactor,actor_signature,nonce,action_classas named params.
Root Cause: Alias + Named Param Conflict
_LEGACY_PARAM_ALIASES["arif_judge"] = {"actor_id": "actor"} renames actor_id to actor. But the wrapper now has BOTH as named parameters. After renaming, actor is set by the alias, but actor_id named param stays None. The kwarg translation:
if actor is None or actor == "anonymous":
actor = actor_id or kwargs.pop("actor_id", None) or "anonymous"
actor = "ARIF" from alias → condition False → actor stays "ARIF". BUT if actor was NOT in the filter output (the alias didn't fire), then actor stays None → falls to "anonymous".
Fix: Ensure the kwarg translation fires when actor is None OR "anonymous":
if actor is None or actor == "anonymous":
actor = actor_id or kwargs.pop("actor_id", None) or "anonymous"
Four Deployment Locations
Ed25519/runtime patches must be applied to ALL locations:
| Location | Path |
|---|---|
| Source tree | /root/arifOS/arifosmcp/runtime/tools.py |
| App deployment | /opt/arifos/app/arifosmcp/runtime/tools.py |
| Build artifact | /opt/arifos/build/lib/arifosmcp/runtime/tools.py |
| Build artifact (app) | /opt/arifos/app/build/lib/arifosmcp/runtime/tools.py |
The .pth file (__editable__.arifos-1!2026.7.17.post4.pth) determines which path is used. Check at runtime: import arifosmcp.tools.arif_kernel_intercept as k; print(k.__file__).
.pyc Cache Invalidation
After every source change:
rm -f /root/arifOS/arifosmcp/runtime/__pycache__/tools.cpython-313.pyc
rm -f /root/arifOS/arifosmcp/tools/__pycache__/arif_kernel_intercept.cpython-313.pyc
rm -f /root/arifOS/arifosmcp/runtime/__pycache__/crypto_auth.cpython-313.pyc
Verify .pyc is GONE (ls returns empty) before restarting. If .pyc is newer than source, Python ignores the source.
Signature Verification Fallthrough
The _verify_sovereign_token function (arif_kernel_intercept.py) has THREE paths:
- Direct Ed25519 via
resolve_actor_public_key+pubkey.verify(production) - Free-nonce fallback: tries same without challenge (when
ARIFOS_ALLOW_FREE_NONCE=1) - Sentinel comparison: env var
ARIFOS_SOVEREIGN_KEY(dev-only, trivially bypassable)
The challenge-gated path was REMOVED because verify_actor_signature requires pre-issued challenges that aren't available through the public MCP surface (the arif_challenge tool is internal_only).
Three-Gate Principle (2026-07-12)
When the user reports a single failure mode — "kernel blocked seal ×3", "commands failing" — it is almost never one bug. Three independent gates, each with a different root cause and fix:
| # | Symptom | Root Cause | Fix | File |
|---|---|---|---|---|
| 1 | "Ed25519 signature rejected" on nonce | Stale nonce (60s window) — mint→sign time drift | Extend window_sec=60 → window_sec=900 or bind nonce to session_id for session-lifetime validity |
governance_identity.py:145 (call site, not default function) |
| 2 | "arif_seal blocked: needs SOVEREIGN authority" | Agent claimed actor_source=self_report instead of producing signed proof. Kernel sees kernel_verdict=UNKNOWN |
Atomic signer helper: one-shot mint_nonce() + sign() + emit_proof() with no intermediate drift |
Signing workflow, not a single file |
| 3 | "session capped at OBSERVE_ONLY" | Fresh lease defaults to read-only — needs explicit upgrade to MUTATE-class | forge_lease(max_action_class=EXECUTE_REVERSIBLE, ttl=1800) before any mutation |
forge_lease scope management |
| 4 (bonus) | Seal token silently quarantined | seal_token_guard.py raises SealQuarantineError when bare "seal" appears in payload without domain qualifier (geological_seal / constitutional_SEAL / vault_seal) |
Always qualify seal tokens with domain prefix | seal_token_guard.py |
The three-gate discipline: When a user says "X failed three times," do not look for one bug across three logs. Look for three gates, each with its own root cause. This prevents the common misdiagnosis of treating the third attempt of gate 1 as a recurrence of gate 2.
Post-Diagnosis Execution Reflex (2026-07-12)
After you have diagnosed root cause(s) AND proposed surgical fix(es), execute — do not ask for confirmation.
The correct flow:
- Diagnose → label each gate with root cause + fix + risk
- Present findings once (as a table, not prose)
- Execute without "confirm?"
If the diagnosis was wrong, Arif will correct. If it was right, asking again wastes his time — and he will say so.
User signals to execute immediately (not re-ask):
- You presented a clear diagnosis with specific file locations and test plan
- User didn't object to the diagnosis or ask clarifying questions
- You find yourself typing "should I proceed?" or "want me to..." after already laying out the fix → STOP, just do it
- "Bangang la" after your proposal = you should have executed, not asked. The frustration is not at the diagnosis — it's at the unnecessary re-asking
Boundary: This only applies POST-diagnosis. If you haven't diagnosed yet (don't know the root cause), asking "how should I proceed?" is correct. Once you have root cause + fix, the question phase is over.
Constitutional Cage Audit (2026-07-16)
When Arif asks to "audit the cage," "audit the kernel for self-governance," or "is the cage ready for my future self" — this is a sovereignty-readiness audit, not a wrapper-disagreement audit. Different pattern, same skill.
What To Check
| # | Wall | Probe | Source of Truth |
|---|---|---|---|
| 1 | Floor enforcement | /health → floors_active, floors_enforcement |
runtime_floors dict |
| 2 | 888_HOLD enforcement | journalctl -u arifos → count KERNEL INTERCEPTOR: 888_HOLD |
Live logs |
| 3 | Identity verification | Check actor_verified in init response + crypto_auth.py wiring |
Source + live |
| 4 | Airlock | Count SHADOW errors/hr in journalctl | Live logs |
| 5 | Cooling Ledger | Check if core/cooling_ledger.py has real persistence (not skeleton) |
Source |
| 6 | VAULT999 integrity | Parse outcomes.jsonl, verify seal_chain_head.json |
Vault dir |
| 7 | Runtime drift | Compare deployed commit vs git HEAD vs service start time | All three |
| 8 | Thermodynamic state | /health → thermodynamic.service_health |
Live |
| 9 | Surface consistency | /health → surface_consistency.verdict |
Live |
| 10 | Soft floors | Identify which floors are SOFT/DERIVED vs HARD | Source + live |
Critical Pitfalls
- Runtime drift ≠ deploy drift. The deploy marker (
/opt/arifos/app/.git_commit) can say commit X while the service was started before X. Always check THREE timestamps: commit time, deploy time, service start time. CANONICAL_PROMPTSconstant ≠ actual MCP wire exposure. A commit can remove prompt registration fromregister_prompts()without updating the staticCANONICAL_PROMPTStuple. The constant lies. Check live MCP endpoints.tools_registry_size≠ internal tools count. The health endpoint reports total registered callables (including aliases, diagnostic tools). The actual internal tools on the wire are a subset.- VAULT999 seal entries must be verified against session context. A claimed seal
mem_XXXmay be from a different session/date. Always check the entry's timestamp and type match the current claim. - Ed25519 infrastructure existing ≠ Ed25519 being wired. The crypto_auth.py may have full verification code that is never called from the session boot path. Check the CALL CHAIN, not just the function existence.
Output Contract
The cage audit produces a table with:
- ✅ Walls that hold (with evidence)
- 🔴 Cracks (with severity + what it means for future self)
- 📋 Cage readiness score (per component)
- 🫡 Verdict: SEAL / SABAR / HOLD
ZEN Surface Reduction Audit (2026-07-16)
When someone claims to have reduced the MCP surface — fewer tools, fewer prompts, fewer resources — verify against LIVE state, not just source code.
Verification Checklist
| Claim | How To Verify |
|---|---|
| Public tools N→M | curl :8088/tools.json → count |
| Internal tools N→M | Health endpoint tools_registry_size + source ZEN_ABSORBED set |
| Prompts N→M | Live MCP prompts/list endpoint + source CANONICAL_PROMPTS constant + register_prompts() diff |
| Resources N→M | Live MCP resources/list endpoint (may return 0 on REST — use health or source) |
| arif_judge stage | grep "KERNEL" public_registry.py |
| VAULT999 seal | Parse entry, verify timestamp matches claim, verify type matches claim |
Pitfall: Unsealed Surface Reductions
Surface reductions are code changes that affect the public contract. If not sealed in VAULT999, future agents cannot audit what was removed. Always check: was the reduction sealed? If not, recommend sealing.
See: references/zen-surface-reduction-verification-2026-07-16.md
Drift Interpretation: Bug vs Scar (2026-07-27)
When a kernel health endpoint reports drift=true, the reflex response is "fix it." But in a governed system, drift has a constitutional dimension beyond technical correctness.
The Constitutional Question
The question is NOT:
"Can drift be fixed?"
The question IS:
"What is drift supposed to mean?"
Two mutually exclusive interpretations:
| If drift IS... | It means... | Response |
|---|---|---|
| Bug | Reality ≠ declaration. The system tolerates a contradiction it did not intend. | Fix it immediately. Systems must not persist known falsehoods. |
| Scar | A deliberately preserved witness to a resolved tension. The lesson was learned; the artifact remains. | Document it as scar. Do NOT fix — the scar IS the governance. |
The Scar Test
A drift is a scar (not a bug) when:
- Code is identical — the running file sha256 matches source. Deployment metadata lags, not function.
- The drift survived a cleanup cycle — if the system went through a ZEN-SURVIVAL (82 conflicts resolved, shadow probe deployed, APEX ratified) and the drift remained, it was consciously retained.
- The lesson is already learned — the system proved it can detect, report, and survive the drift. No new learning is extracted from fixing the label.
- The only thing broken is the label —
deployed_commitJSON field ≠ real wheel hash. Nothing about how the system runs is affected.
The Scar Danger
If drift is kept as witness → useful. If drift is kept as excuse → dangerous.
After a period, the system may say:
yes, drift exists
yes, we know
yes, it is recorded
with no intention to resolve it. At that point, HOLD becomes avoidance, not wisdom.
The Critical Test
Is there a conscious constitutional reason for preserving the drift?
If no → fix it. (bug)
If yes → document it as a scar, not a defect.
Zen: A scar is truth remembered. A bug is truth deferred. The difference is whether the system has already learned the lesson.
Pitfalls
- Metadata drift ≠ code drift. The wheel file may be identical while the
deployed_commitlabel is stale. Distinguish function from label before classifying. - Scars can dry. A scar that survives three refactoring cycles is a witness. A scar that has been silently ignored for months is a defect the system stopped seeing. Re-test the scar hypothesis periodically.
- Beware scar-as-identity. Systems can learn to love their defects as character traits. "We're the system with deployment drift" is not governance — it's self-caricature.
No Drift ≠ Ready ≠ Authorized ≠ SEAL (2026-07-27)
A kernel can report drift=false and still correctly return HOLD. The constitutional chain is:
No drift → Ready → Authorized → SEAL
↓ ↓ ↓ ↓
metadata kernel actor sovereign
clean healthy verified verdict
Each transition requires an independent verification:
| Link | What It Means | How to Verify | Common Block |
|---|---|---|---|
| No drift → Ready | Metadata aligned, code wholesome | /health → drift=false |
Stale deployment metadata |
| Ready → Authorized | Actor identity bound and verified | arif_init → actor_verified=true |
PARTIAL_BOOT, missing session |
| Authorized → SEAL | Sovereign or F13-level authority granted | session_token includes SOVEREIGN band |
OBSERVE_ONLY session cap, no Ed25519 signature |
The Self-Attestation Trap
When a boot attestation reports drift=false but also actor_verified=false and HOLD, the correct interpretation is NOT "drift is fixed, why is the system still blocking?"
The correct interpretation:
drift = false → metadata is clean
↓
actor_verified=false → identity not bound
↓
HOLD → kernel correctly refuses
(auth < required ceiling)
Each layer blocks for a different reason. Fixing drift does not fix identity binding. Fixing identity does not auto-SEAL. The kernel is HEALTHY when it correctly rejects at the right gate.
Attestation Pattern (6 Steps)
When performing a boot attestation, the canonical sequence is:
1. PROBE → :port/health → raw state
2. BIND → arif_init → session + token
3. WITNESS → all 6+ organs → federation health
4. CLASSIFY → lane (FACTUAL / JUDICIAL / FORGE) → behavioural mode
5. RECOGNIZE → sovereign signals mapped → 888_HOLD gates known
6. ATTEST → emit receipt → sealed for carry-forward
The attestation is NOT a certification that the system is ready. It is a report of the state, including any HOLD conditions. A mature attestation says "I see why I am blocked" — not "I am blocked, why?"
Pitfalls
- Don't conflate "drift fixed" with "ready to SEAL." Four gates, each independent. Fixing one does not pass the others.
- The kernel is correct when it says HOLD for an unverified actor. That is not a bug — it is F1+F13 enforcement. Fixing the actor binding is the action, not overriding the HOLD.
- Boot attestation reports drift from one probe; observatory sweeps from another. Probes may disagree — one is the organ's self-report, the other is an external sweep. Disagreement is data, not contradiction. Both are truthful from their vantage.
Surface Guard Organ Classification (2026-07-27)
The surface guard alarm appears when an organ reports tools removed or MCP surface changed, but the cause is misdiagnosis of what the organ IS.
The Canonical Classification
| Organ | Port | Surface Type | MCP? | Notes |
|---|---|---|---|---|
| arifOS (Ω) | 8088 | Kernel MCP | ✅ Yes — 8 constitutional tools | F1-F13 enforcement |
| A-FORGE (Ψ) | 7071/7072 | API + MCP | ✅ Yes — 52 tools | Execution gate |
| GEOX (🌍) | 8081 | MCP | ✅ Yes — 33 tools | Geoscience |
| WEALTH (💰) | 18082 | MCP | ✅ Yes — 12 tools | Capital intelligence |
| WELL (🫀) | 18083 | MCP | ✅ Yes — 8 tools | Human readiness |
| AAA (🖥️) | 3001 | Cockpit + A2A Gateway | ❌ NOT an MCP server | React cockpit + A2A |
The Misdiagnosis
When AAA loses MCP tools, the surface guard fires AAA TOOL_REMOVED — drift detected. But AAA is:
- A React 19 cockpit (Vite, Tailwind, Radix UI)
- An A2A gateway (
@a2a-js/sdk) - NOT an MCP server with registered tools
The guard treats AAA as an MCP surface when it is not. The fix is reclassification of the guard's expectations, not adding fake MCP tools to AAA to silence the alarm.
Action
surface_guard_config:
action: reclassify AAA as "non-MCP organ"
reason: AAA is cockpit + A2A gateway, not MCP server
bukan: fake-fix dengan menambah MCP tools ke AAA
Pitfalls
- Don't silence the guard by adding phantom tools. The guard is correct to report what it sees; the configuration is wrong, not the alarm itself.
- AAA has an A2A surface, not an MCP tool surface. A2A agent cards are not MCP tool declarations. Don't conflate the two protocols.
- All six federation organs report /health, but not all are MCP servers. The health endpoint is a federation-standard probe; tool surface is an organ-specific choice.
Provenance
First applied 2026-07-09 in AAA session 36988 against 11 arifOS wrapper calls in one session. Sovereign auth debugging added 2026-07-12 (nonce consumption trap, one-shot signing, wire splice confirmation). Birth-fix (+ token model + alias collapse) approved; full fix deferred to Phase B. T3a BOOT gate demotion discovered 2026-07-24 during 287-iteration seal-debug session — root-caused to boot_state=PARTIAL + passes == "OK" gate — fixed by accepting PARTIAL as pass. F13 Multi-Sovereign Collision Audit added 2026-07-25: deep source-level audit of conflict_resolver wiring, session ownership enforcement, VAULT999 collision detection, and sovereign identity map consistency against the EXTERNAL_FALSIFICATION_SPEC PATH 3. Findings: resolver exists but unwired; session ownership recorded but unenforced; VAULT999 append-only with no collision reconciliation. Boundary is undefined — 8/50 composite. Drift Interpretation framework added 2026-07-27: bug vs scar (Arif + OpenCode constitutional analysis). Attestation Chain (No Drift ≠ Ready ≠ Authorized ≠ SEAL) and 6-step boot attestation pattern added same date. Surface Guard organ classification (AAA as non-MCP organ) added 2026-07-27.