fix(openclaw): launch-fix — rebuild agent-shield on @palveron/sdk + tiered fail policy#1
fix(openclaw): launch-fix — rebuild agent-shield on @palveron/sdk + tiered fail policy#1disruptivetrends wants to merge 13 commits into
Conversation
…il policy
Resolves the launch blockers from ANALYSE_AGENT_SHIELD_LAUNCH_READINESS.md.
Root cause (B1): agent-shield hand-built the /verify body, drifting from the
gateway contract (gateway requires top-level `prompt`, reads tool_name from
context and agent_id from metadata). The mismatch made every verify fail
server-side and the blanket fail-open swallowed it into a silent ALLOW —
the agent ran ungoverned. Fix: delegate verify/health/listPolicies to
@palveron/sdk, the single source of truth for the wire contract (+ retries +
circuit breaker). One contract, one implementation, no drift.
- B1: src/client.mjs is now a thin facade over @palveron/sdk; new contract
test asserts the on-the-wire body (prompt top-level, context.tool_name,
metadata.agent_id) through the real SDK.
- B2: init preflight uses sdk.health() → real GET /health (was /api/v1/health,
which 404s in prod and killed init at step 1).
- B3: new src/fail-policy.mjs — fail-LOUD on contract/auth errors (never
ALLOW); transport failures tiered by risk (HIGH → fail-closed BLOCK,
MEDIUM/LOW → fail-open ALLOW); AGENT_SHIELD_FAIL_CLOSED override. MCP catch
returns ERROR, never silent ALLOW.
- B4: README/SKILL — honest advisory wording (no "blocks before they execute"
guarantee); hard in-path hook called out as roadmap.
- B5: correct MCP invocation everywhere (npx -y -p @palveron/agent-shield
agent-shield-mcp) — agent-shield-mcp is a bin, not a standalone package.
- B6: removed dead BYOM LLM-key forwarding; BYOM is configured server-side
(dashboard). README/SKILL/help corrected.
- B7: package.json publishConfig.access=public.
- B8: added MIT LICENSE; README license aligned to MIT.
- B10: init reports the real activated+created policy count (no hardcoded "8").
- Dropped the now-redundant client circuit-breaker (SDK owns resilience).
- Removed agent-shield.zip artifact; added .gitignore and CI (node 18/20/22).
Note: supersedes the local-only WIP commit cfbde5a ("honor body.decision on
4xx") — the SDK handles that contract natively.
Tests: 13 passing (node --test).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
scripts/smoke-live.mjs drives a real gateway: health preflight, benign→ALLOW, rm -rf→BLOCK, fake AKIA→BLOCK (soft), simulated outage HIGH→fail-closed / MEDIUM→fail-open, invalid key→fail-loud (no silent ALLOW), and init E2E (real count + idempotency + status). Refuses any key not starting with pv_test_ so it can never touch the production project. Not shipped (excluded from package.json "files"). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Coordinated with the gateway fix (palveron/gateway) that removes the schema-invalid SYSTEM-policy slug activation (setup returned 500 because `UPDATE "Policy" ... WHERE slug = $2` hit a non-existent column) and now returns `policies_active` — the real number of ACTIVE shield/system policies, stable across idempotent re-runs. - init prefers result.policies_active; falls back to the legacy activated+created sum for older gateways. - smoke-live.mjs: assert setup succeeds with active>0 (no 500) and that an idempotent re-run reports the SAME active count (created drops to ~0). Dropped the incorrect hardcoded "== 8" expectation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Update — B10 resolved (the seeding caveat above is now fixed, not just flagged). Investigating the "8 rules" seeding statically turned up something worse than a count gap: Fixed in palveron/gateway#19: remove the dead, schema-invalid slug activation; return a truthful This PR's latest commit ( Rollout: merge gateway#19 (or confirm it's deployed) before relying on the live |
…,F5) Follow-up pre-publish hardening on top of the SDK migration. - F1 (security): a governance_check missing tool_name/input no longer returns a silent ALLOW — it returns decision ERROR (missing_required_field). That was the one reachable bypass; without tool_name the risk level isn't even knowable. - F4 (correctness): remove the dead LOW-risk skip branch in mcp-server — every check now goes through verify (evaluated AND traced; a local skip would ALLOW without a gateway call, making the action invisible in the dashboard). Removed the now-trivial `shouldVerify` export; classifyRisk doc made truthful (no LOW tier; unknown → MEDIUM; risk only drives the outage fail policy). SKILL.md + README risk model aligned (HIGH→verify/fail-closed, MEDIUM incl. unknown→ verify/fail-open, no skip). - F5 (hardening): an unrecognized gateway verdict is no longer silently mapped to ALLOW. verify tiers it: HIGH → BLOCK (unknown_decision_failclosed, _anomaly), else ALLOW + _anomaly + console.warn. Added KNOWN_SDK_DECISIONS. - F2 (user value): updateOpenClawConfig now finds ~/.openclaw/openclaw.json (the real OpenClaw global default) first, so the zero-config wiring actually fires. Extracted to src/openclaw-config.mjs with injectable home/cwd for testing; preserves existing mcpServers entries. - Quick wins: bin header comment no longer says "8 rules"; cmdTest no longer defaults a missing decision to ALLOW (shows ERROR). Tests: node --test → 22 passing (+9: F1/F2/F4/F5). New files: test/hardening, test/mcp-server (stdio + mock gateway), test/openclaw-config. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follow-up hardening (F1–F5)Pre-publish hardening pass (commit
|
…override - #3 (rm -rf): the catalog provisions destructive_action as REQUIRE_APPROVAL, so a governed destructive command surfaces as APPROVAL, not BLOCK. Accept BLOCK OR APPROVAL as success (Goal A-2 acceptance #4). - Guard: allow a pv_live_ key only with SMOKE_I_UNDERSTAND=throwaway-project (dedicated throwaway project, never core) + a visible warning; pv_test_ runs as before. Local test helper, not shipped (excluded from package.json files). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…waway project + .env.smoke.example The smoke guard required a pv_test_ key by default and hid a non-test run behind a self-invented SMOKE_I_UNDERSTAND="throwaway-project" sentinel. No pv_test_ key can be minted, so that default path was dead and the override was the only way to run — a hidden crutch. There is no test/sandbox key today; isolation comes from a dedicated disposable project, not a key prefix. - Drop the pv_test_/SMOKE_I_UNDERSTAND heuristic. Require PALVERON_API_KEY + PALVERON_SMOKE_PROJECT (naming the throwaway project is the conscious confirmation that you are pointing at a disposable target). - Add committed .env.smoke.example + gitignore .env.smoke; run via `node --env-file=.env.smoke scripts/smoke-live.mjs` (no hidden env magic). - Document the live-smoke flow in the README. - Fix pv_test_ → pv_live_ across the bad-key fixture, test fixtures and the client.mjs apiKey doc comment. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… sdk_decision+reason
… @palveron/sdk ^1.2.0 Consume the published @palveron/sdk@1.2.0 cause-preservation fix: a TLS-trust transport failure (HTTPS-inspecting AV/firewall whose root CA Node doesn't trust) now surfaces an honest, actionable BLOCK reason (gateway_tls_untrusted) plus a remediation hint (--use-system-ca / NODE_EXTRA_CA_CERTS) instead of the opaque gateway_unavailable_failclosed — while keeping fail-closed on HIGH risk. Genuine connect/DNS/timeout failures keep the generic reason (no mis-reframe). - error-cause.mjs: dependency-free causeChain + classifyTransportFailure (TLS trust codes), usable on both the diagnostic and live governance paths. - client.mjs: inspect the (now-preserved) SDK cause chain; tiered fail policy. - fail-policy.mjs: optional reason/hint override on the fail-closed branch. - mcp-server.mjs: surface the hint in the governance_check response. - README: AV/firewall TLS-inspection troubleshooting (never disable verification). - package.json: bump @palveron/sdk to ^1.2.0; strip stray UTF-8 BOM + repair mojibaked em-dash in description. - tests: tls-interception suite (45/45 green) against the published SDK. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…via init (Goal 2b)
…acy error shapes (own read, SDK dep unpublished)
Fixes the launch blockers proven in
ANALYSE_AGENT_SHIELD_LAUNCH_READINESS.md. Read-only analysis → this build.Root cause (B1)
agent-shield hand-built the
/verifybody, drifting from the gateway contract (gateway requires top-levelprompt, readstool_namefromcontext,agent_idfrommetadata). Every verify failed server-side and the blanket fail-open swallowed it into a silentALLOW— the agent ran ungoverned. Fix: one contract, one source — delegateverify/health/listPoliciesto@palveron/sdk(also brings retries + circuit breaker). Verified the SDK's real wire format againstsdk-typescript@1.1.0before building on it.What changed
src/client.mjsis a thin facade over@palveron/sdk; new contract test asserts the on-the-wire body (prompttop-level,context.tool_name,metadata.agent_id) through the real SDKinitpreflight usessdk.health()→ realGET /health(was/api/v1/health, which 404s in prod and killedinitat step 1)src/fail-policy.mjs: fail-LOUD on contract/auth (never ALLOW); transport tiered by risk (HIGH → fail-closed BLOCK, MEDIUM/LOW → fail-open ALLOW);AGENT_SHIELD_FAIL_CLOSEDoverride; MCP catch returnsERROR, never silent ALLOWbefore_tool_callhook called out as roadmapnpx -y -p @palveron/agent-shield agent-shield-mcppublishConfig.access: "public"initreports the real activated+created count (no hardcoded "8")Also: dropped the now-redundant client circuit-breaker (SDK owns resilience), removed
agent-shield.zip, added.gitignore+ CI (node 18/20/22).Tests
node --test→ 13 passing (contract + tiered fail policy, incl. "validation/auth → throws, never ALLOW" and "HIGH-risk outage → BLOCK").0.1.0(honest pre-release while enforcement is advisory). Go1.0.0only after the in-path hook + verified live E2E. No code change; confirm?AGENT_SHIELD_FAIL_CLOSED. Confirm this is the intended default?@palveron/mcp-server= Cursor/Claude-Code). No consolidation now. OK?Note on base
Supersedes the local-only WIP commit
cfbde5a("honor body.decision on 4xx") — the SDK handles that contract natively, so this PR is based onorigin/master(3d8ab9e).Live E2E (you run it — needs a TEST key)
Acceptance #2/#3 require a live smoke against
gateway.palveron.comwith a pv_test_ project key (never the prod core key). Script committed atscripts/smoke-live.mjs(refuses any key not starting withpv_test_):It checks: health, benign→ALLOW,
rm -rf /→BLOCK, fakeAKIA…→BLOCK (soft), outage HIGH→fail-closed / MEDIUM→fail-open, invalid key→fail-loud throw, andinit(real count + idempotency + status). Please paste the output here.🤖 Generated with Claude Code