Skill: hastodev/muhaven-rwa-skill (version 0.1.4)

--- name: muhaven-rwa-skill display_name: MuHaven RWA Portfolio version: 0.1.4 schema_version: "1.0" description: | Confidential real-world-asset (RWA) portfolio agent built on MuHaven's Fhenix-CoFHE-encrypted token primitives. Read your encrypted balances, stage yield claims, draft buys + claims for human confirmation. Position tools NEVER auto-submit — every state-mutating action goes through a three-tier confirmation surface (inline button ≤$200, Mini App + 6-digit OTP $200-$5K, deep-link passkey >$5K). license: MIT homepage: https://muhaven.app documentation: https://github.com/hasToDev/muhaven/tree/master/packages/openclaw-skill support: https://github.com/hasToDev/muhaven/issues authors: - name: MuHaven email: [REDACTED_SECRET] url: https://muhaven.app maintainers: - [REDACTED_SECRET] keywords: - fhe - fhenix - rwa - muhaven - openclaw - confidential-defi - erc-3643 runtime: type: node node: ">=20" entry: dist/index.cjs permissions: network: egress_allowlist: - https://api.muhaven.app - https://muhaven.app deny_default: true filesystem: read: [] write: [] process: spawn: [] secrets: storage: os_keychain references: - name: muhaven_jwt owner: muhaven-broker scope: - mcp.read.* - mcp.propose.* mcp: bundled: "@muhaven/mcp" # Exact version pin — `pnpm pack` rewrites `workspace:*` in package.json # to this exact value, which ClawHub consumers resolve from npm. The # `verify-subset.ts` triple-match enforces parity between this field, # `manifest.json#mcp.bundled_version`, and `packages/mcp/package.json#version`. # An earlier `bundled_minor_pin: true` claim was removed 2026-05-11 # pre-publish review: the runtime + CI gates enforce exact equality, # not semver-minor-range; the field name implied a softer pin than the # code actually delivered. bundled_version: 0.1.5 toolset_subset: - muhaven.read.portfolio - muhaven.read.yields - muhaven.read.distribution - muhaven.read.tokens - muhaven.read.audit - muhaven.read.protection_coverage - muhaven.read.kyc_attestation - muhaven.position.buy - muhaven.position.claim - muhaven.policy.pause - muhaven.policy.session_key_status toolset_excluded: - muhaven.position.sell - muhaven.position.rebalance - muhaven.policy.set_tier - muhaven.policy.audit_export - muhaven.issuer.distribute_yield - muhaven.issuer.kyc_add - muhaven.issuer.kyc_remove - muhaven.issuer.unpause_token - muhaven.issuer.audit_query - muhaven.governance.propose - muhaven.governance.cast_vote toolset_excluded_reason: | Exclusions are deliberate per ADR-C — the OpenClaw surface is investor-buy-and-claim, not portfolio-management. `set_tier` requires the dashboard passkey ceremony (cannot be a Telegram callback); `audit_export` is operator-shaped and lives behind the dashboard. `sell` + `rebalance` raise the blast radius of a single misclick on a Telegram inline button — defer to Wave 5 once we have soak data. `issuer.*` (Wave 4 P7) is issuer-only — the OpenClaw surface is investor-facing; issuer flows live on HavenBot in-dashboard + standalone `@muhaven/mcp` install. `governance.*` (Wave 4 P11) requires the dashboard ConfirmModal + cofhe encrypt ceremony, neither of which the Telegram-bot surface can drive. Investors who want to vote follow the dashboard flow. sandbox: runtime: nemoclaw forward_compat: - issue: "openclaw#28298" compat_field: permissions - issue: "openclaw#28360" compat_field: sandbox publishing: registry: clawhub trusted_publishing: provider: github workflow: ".github/workflows/openclaw-skill-publish.yml" oidc_audience: "openclaw://clawhub-trusted-publish" signing: provider: sigstore artifact: "muhaven-rwa-skill-{version}.tgz" bundle: "muhaven-rwa-skill-{version}.tgz.sigstore" required_reviewers: 2 threat_model_ref: "../../development/DEV_WAVE_4/THREAT_MODEL_P0.md" --- # MuHaven RWA Portfolio — OpenClaw skill This skill bundles a curated subset of `@muhaven/mcp` plus an OpenClaw-shaped config bundle. It runs in OpenClaw's NemoClaw runtime (or any MCP host that honours the manifest's `permissions` block) and connects to the live MuHaven backend at `https://api.muhaven.app`. ## What it does - **Reads your encrypted RWA portfolio** — balances stay encrypted with Fhenix CoFHE; the skill never sees plaintext. - **Stages buy + claim intents** for the OpenClaw surface — the skill never auto-submits. Every intent emits a structured confirmation request to one of three tiers based on amount. - **Surfaces audit log** for compliance / forensics. - **`/pause` kill-switch** uninstalls the on-chain `@zerodev/permissions` validator within one Arb block. ## What it intentionally cannot do - Move funds without your passkey. The skill issues unsigned UserOp envelopes; signing happens in the `muhaven-broker` daemon (≤$200 inline callback) or in your dashboard / Mini App (>$200 tiers). - Speak to anything outside the egress allowlist. `manifest.json`'s `network.deny_default: true` means a tampered binary cannot exfiltrate to a third party. - Read or write your filesystem. `permissions.filesystem.{read,write}: []`. - Spawn child processes. `permissions.process.spawn: []`. - Store any secret. JWT lives in `muhaven-broker`'s OS-keychain entry; the skill calls the broker over Unix-socket / named-pipe IPC. ## How to install 1. Install plain OpenClaw + ClawHub CLI globally: ```bash npm install -g openclaw@latest clawhub openclaw --version # confirm install clawhub --version ``` (Runtime decision 2026-05-11: plain OpenClaw under `sandbox.fallback: host_native`. NemoClaw remains the preferred runtime claim in `manifest.json` for forward-compat; today's deploy targets plain OpenClaw.) 2. Install the broker daemon **separately as a global** so its bin lands on `$PATH` regardless of ClawHub's bin-handling: ```bash npm install -g @muhaven/mcp@0.1.5 muhaven-broker --version # sanity check ``` (The skill itself bundles `@muhaven/mcp` inline since 0.1.1 via tsup `noExternal`, so no separate `npm install --omit=dev` is needed after `clawhub install`. The global install of `@muhaven/mcp` is still useful because it puts the `muhaven-broker` daemon bin on `$PATH` — the skill's bundled copy of `@muhaven/mcp` only exposes the MCP server, not the daemon CLI.) 3. Install the skill: ```bash clawhub install muhaven-rwa-skill@0.1.4 ``` 4. One-shot broker setup + passkey login: ```bash muhaven-broker setup ``` This applies sensible env defaults, mints an ephemeral session key, spawns the daemon detached, and runs the device-code login — opens `https://muhaven.app/link?code=XXXX-XXXX` in your browser for passkey approval. The daemon stays running after `setup` returns. Use `muhaven-broker setup --foreground` if a supervisor will manage the daemon process. 5. Optional: link your Telegram account for the `/agent/openclaw/*` confirmation surface. From the dashboard `/agent` page → Telegram tab → "Link Telegram" → message the bot at `@muhaven_bot` with the one-time link code. ## Confirmation tiers The skill never executes a state-mutating action without a confirmation. Three tiers based on intent notional (USDC): | Range | Surface | Why | |---|---|---| | **≤ $200** | Telegram inline keyboard "Confirm" button | Low blast radius. Same trust model as a $200 mobile wallet payment — single-tap inline. | | **$200 – $5,000** | Mini App with 6-digit OTP sent via separate Telegram message | Defends against a chat-stuffing attack where the LLM emits a `Confirm` button users tap on autopilot. OTP is out-of-band. | | **> $5,000** | Deep-link to dashboard `https://muhaven.app/agent/confirm?intent=…` for passkey signature | Phishing-resistant by construction — WebAuthn RP-ID is bound to the dashboard origin; a Telegram-based MITM cannot complete passkey. | Tier boundaries are audit-logged in `agent_audit_events` with the amount-bucket the intent fell into. Investors can lower the boundaries in the dashboard `/agent` policy tab; they cannot raise them above the hardcoded ceilings (regulatory + Reg BI Care Obligation). ## Hardening invariants (do NOT relax without audit) - `permissions.network.deny_default: true` — every new endpoint requires a manifest update + signed re-publish. - `permissions.secrets.storage: os_keychain` — paste-token UX is forbidden. - `runtime.type: node` — no shell, no Python, no JIT-compiled blob. - `mcp.toolset_subset` is the only set of tools the skill will dispatch to — additions require an ADR + signed re-publish. - Sigstore signing + GitHub OIDC trusted publishing — long-lived ClawHub tokens are not used. ClawHavoc (Feb 2026) precedent. - `required_reviewers: 2` — single-maintainer publish is rejected at the policy gate. Two-maintainer release is the lesson from the Anthropic MCP SDK STDIO arbitrary-command CVEs (Apr 2026). ## Tool inventory (subset of `@muhaven/mcp`) See `manifest.json` and the upstream descriptors in `@muhaven/mcp/src/tools/descriptions.ts`. The skill only re-advertises the `mcp.toolset_subset` listed in this frontmatter; descriptor SHA-256 hashes are pinned in `tool-hashes.json` and verified on every skill load (mcp-context-protector pattern, post-MCPoison). ## Reference docs - ADR-C in `development/research-docs/WAVE_4_AGENTIC_RESEARCH_RESULT.md` - `development/DEV_WAVE_4/TOOL_NAMESPACE.md` for the full naming surface - `development/DEV_WAVE_4/THREAT_MODEL_P0.md` for OWASP LLM + Agentic mappings ## License MIT. See `LICENSE` in the repository root.

path: bin/muhaven-rwa-skill.cjs
content: #!/usr/bin/env node
/* eslint-disable */
const { runOpenClawSkill } = require('../dist/index.cjs');

runOpenClawSkill().then(
  () => process.exit(0),
  (err) => {
    process.stderr.write(`fatal: ${err && err.stack ? err.stack : String(err)}\n`);
    process.exit(1);
  },
);


path: CHANGELOG.md
content: # Changelog

All notable changes to `muhaven-rwa-skill` (the OpenClaw skill bundling
`@muhaven/mcp`) will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.1.4] — 2026-05-17

Re-bundles against `@muhaven/mcp@0.1.5` so the new `muhaven-broker
stop` subcommand is available to skill consumers (who also install
`@muhaven/mcp` globally for the broker bin per SKILL.md "How to
install"). No tool-subset or sandbox changes; passthrough version bump.

### Changed

- **Bundled `@muhaven/mcp` bumped 0.1.4 → 0.1.5** (`manifest.json#mcp.bundled_version`
  + `SKILL.md` frontmatter + the `verify-subset.ts` triple-match guard).
  Operators following SKILL.md should `npm install -g @muhaven/mcp@0.1.5`
  so the new `muhaven-broker stop` is on `$PATH` alongside the existing
  `muhaven-broker setup`.

## [0.1.3] — 2026-05-17

Re-bundles against `@muhaven/mcp@0.1.4` so the new
`muhaven-broker setup` subcommand is available to skill consumers who
also install `@muhaven/mcp` globally for the broker bin (per
SKILL.md "How to install" prerequisite). No tool-subset or sandbox
changes; this is a passthrough version bump that flows the upstream
0.1.4 release into the published skill bundle.

### Changed

- **Bundled `@muhaven/mcp` bumped 0.1.3 → 0.1.4** (`manifest.json#mcp.bundled_version`
  + `SKILL.md` frontmatter + the `verify-subset.ts` triple-match guard).
  Operators following the SKILL.md install runbook should
  `npm install -g @muhaven/mcp@0.1.4` so the `muhaven-broker setup`
  bin is on `$PATH`.
- **`SKILL.md` install steps** updated to reference `setup` as the
  one-shot install path; the prior five-line manual broker ritual is
  no longer the documented happy path.

## [0.1.2] — 2026-05-17

Cosmetic fix — corrects the display name shown on ClawHub. No code or
manifest schema changes; bundled `@muhaven/mcp@0.1.3` is identical to
0.1.1.

### Fixed

- ClawHub display name now reads "MuHaven RWA Portfolio" (matching
  `manifest.json#display_name`). 0.1.1 was published without the
  `clawhub publish --name "<...>"` flag, which caused clawhub v0.12.3 to
  fall back to the path basename (`package` from `pnpm pack` output) and
  render the skill title as "Package". The fix is to always pass
  `--name` explicitly on publish; recipe codified in memory
  `feedback_clawhub_publish_explicit_name`.

## [0.1.1] — 2026-05-16

Q2 fix bundle from the post-§4 queue closing the ClawScan findings and
the `npm install --omit=dev` install-time gap. Pins `@muhaven/mcp@0.1.3`
which carries the broker-side fixes for the §3e⁶ broker-session-key,
broker-env-divergence, and serverInfo-version findings.

### Added

- **`SECURITY.md`** at the package root, documenting:
  - host_native vs NemoClaw posture (the published artifact runs against
    plain OpenClaw under `sandbox.fallback: host_native` per operator
    decision 2026-05-11; `manifest.permissions` are advisory in that
    mode, not kernel-enforced).
  - Defense-in-depth measures that DO work in host_native mode
    (tool-description hash pinning, broker JWT-scope check, three-tier
    confirmation, STDIO-only transport).
  - NemoClaw upgrade path (forward-compatible — no skill change required).
  - Operator posture requirements (session-key provenance, broker
    reachability, JWT presence).
  - Network egress allowlist + reason for each pinned host.
  - Filesystem + spawn posture (deny-all).
  - Vulnerability reporting channels.
  - Closes ClawScan #1 (host_native trade-off documented in a
    scanner-visible file, not just `$comment` JSON keys).

### Changed

- **`tsup.config.ts` now inline-bundles `@muhaven/mcp`** via
  `noExternal: ['@muhaven/mcp']`. The published tarball is self-contained
  — ClawHub install no longer needs an out-of-band `npm install --omit=dev`
  to resolve the transitive dep. Dist size grows from ~25KB → ~686KB; the
  added bytes are entirely the @muhaven/mcp + viem + zod + MCP SDK
  transitive graph. Closes ClawScan #3 + §3e⁶
  F-clawhub-install-no-npm-install (HIGH).
- **`muhaven.policy.pause` tool description tightened** to "Activate
  /pause kill-switch (uninstallPlugin). NEVER auto-submits — requires
  user confirmation." (was "Activate /pause kill-switch
  (uninstallPlugin)."). The description is what the LLM sees and surfaces
  to the user; the "NEVER auto-submits" assertion now appears at the LLM
  tool-call layer alongside the existing config-layer assertion. Closes
  ClawScan #2.
- **Pinned `@muhaven/mcp@0.1.3`** (was 0.1.2). The new MCP version
  carries: lazy broker-session-key validation, `muhaven-broker login
  --from-daemon` flag, build-time `serverInfo.version` inject. None of
  these changes affect the skill's API surface — the tool subset stays
  identical.

### Tests

- 15/15 vitest pass (manifest-consistency × 3 + subset × 9 + lifecycle × 3).
- Manifest-consistency triple-match enforced at vitest gate time:
  `manifest.json#mcp.bundled_version === SKILL.md#mcp.bundled_version ===
  packages/mcp/package.json#version === 0.1.3`.

## [0.1.0] — 2026-05-12

First publishable cut to ClawHub. See `development/DEV_WAVE_4/DEV_LOG.md`
2026-05-12 entry for the publish-ceremony walkthrough.

### Added — Tools (11 subset)

- `muhaven.read.*` (7): `portfolio` · `yields` · `distribution` · `tokens` ·
  `audit` · `protection_coverage` · `kyc_attestation`
- `muhaven.position.*` (2): `buy` · `claim`
- `muhaven.policy.*` (2): `pause` · `session_key_status`

### Added — Infrastructure

- OpenClaw `manifest.json` (schema_version 1.0) declaring permissions
  block + four `user_config` entries (`backend_url`, `dashboard_url`,
  `broker_endpoint`, `read_only`).
- ClawSecure `config.json` mirroring permissions for kernel-enforced
  NemoClaw runtimes.
- Tool-subset triple-match gate (`scripts/verify-subset.ts`) enforcing
  `src/index.ts:TOOLSET_SUBSET === manifest.json#mcp.tool_subset ===
  SKILL.md#mcp.toolset_subset`.

### Distribution

- Published to ClawHub as `muhaven-rwa-skill-rehearsal@0.1.0-rc.1` (the
  rehearsal slot); promoted to `muhaven-rwa-skill@0.1.0` upon §3 Path (a)
  walkthrough closure.

[Unreleased]: https://github.com/hasToDev/muhaven/compare/openclaw-skill-v0.1.2...HEAD
[0.1.2]: https://github.com/hasToDev/muhaven/releases/tag/openclaw-skill-v0.1.2
[0.1.1]: https://github.com/hasToDev/muhaven/releases/tag/openclaw-skill-v0.1.1
[0.1.0]: https://github.com/hasToDev/muhaven/releases/tag/openclaw-skill-v0.1.0


path: config.json
content: {
  "$comment": "ClawSecure-style runtime config for muhaven-rwa-skill. Loaded by the NemoClaw sandbox at boot and supplements the manifest's permissions block. Values here are NOT user-configurable — they ship with the signed skill bundle and any change forces a re-publish + re-sign + re-scan.",
  "$schema": "https://openclaw.dev/schemas/clawsecure/1.0.json",
  "schema_version": "1.0",
  "skill": "muhaven-rwa-skill",
  "version": "0.1.4",
  "execution": {
    "user": "skill-runner",
    "cwd": "/run/openclaw/muhaven-rwa-skill",
    "umask": "0077",
    "rlimit": {
      "fsize_bytes": 0,
      "nofile": 64,
      "nproc": 0,
      "memory_bytes": 268435456
    }
  },
  "network": {
    "$comment_egress_advisory_in_host_native": "Mirrors manifest.json#permissions.network — prod hosts only. Staging rehearsals (§3 Path (a) in AGENTIC_TEST_PLAN.md) install the same signed artifact with `user_config.backend_url=https://api-stage.muhaven.app` and rely on plain OpenClaw `sandbox.fallback: host_native` (advisory, not kernel-enforced). A future runtime that enforces this allowlist would block the staging override; that is intentional — the published artifact is prod-pure.",
    "egress_allowlist": [
      {
        "host": "api.muhaven.app",
        "port": 443,
        "protocol": "https",
        "purpose": "MuHaven backend — REST API + agent policy + intent confirmation"
      },
      {
        "host": "muhaven.app",
        "port": 443,
        "protocol": "https",
        "purpose": "MuHaven dashboard origin — deep-link target for >$5K confirmations and device-code authorization (/link)"
      }
    ],
    "egress_default": "deny",
    "ingress_listen": "deny",
    "dns": {
      "resolver": "system",
      "deny_dynamic_allowlist_resolution": true
    }
  },
  "filesystem": {
    "read": [],
    "write": [],
    "tmp_read_write": "deny",
    "follow_symlinks": false
  },
  "process": {
    "exec": "deny",
    "fork": "deny",
    "ptrace": "deny",
    "signals_inbound": ["SIGTERM", "SIGINT"],
    "signals_outbound": "deny"
  },
  "ipc": {
    "unix_socket": {
      "$comment_path_pattern_alignment": "Path pattern mirrors `defaultBrokerEndpoint()` in `@muhaven/mcp/src/config.ts` (POSIX: `${HOME}/.muhaven/broker.sock`; Windows: named pipe). The pre-2026-05-12 pattern `${XDG_RUNTIME_DIR:-/run/user/${UID}}/muhaven-broker.sock` failed silently on macOS (no `XDG_RUNTIME_DIR` default + no `/run/user/${UID}`) and Windows (no `$UID`), so a kernel-enforced runtime would have refused the broker IPC on those platforms. Today this field is host_native-advisory, but it now aligns with the broker code so a future kernel-enforced runtime passes through.",
      "allow_connect": [
        {
          "purpose": "muhaven-broker daemon",
          "path_pattern": "${HOME}/.muhaven/broker.sock",
          "windows_named_pipe": "\\\\.\\pipe\\muhaven-broker-*"
        }
      ]
    },
    "shared_memory": "deny"
  },
  "secrets": {
    "storage_backend": "os_keychain",
    "deny_paste_ui": true,
    "deny_env_block": true,
    "deny_clipboard_io": true,
    "references": [
      {
        "name": "muhaven_jwt",
        "owner": "muhaven-broker",
        "audience": ["mcp.read.*", "mcp.propose.*"],
        "ttl_sec": 86400
      }
    ]
  },
  "telemetry": {
    "stdout_capture": "audit_only",
    "stderr_capture": "audit_only",
    "stdout_egress": "deny",
    "stderr_egress": "deny",
    "metrics": "deny"
  },
  "audit": {
    "tool_invocations": "log",
    "denied_egress": "log_and_block",
    "denied_filesystem": "log_and_block",
    "denied_spawn": "log_and_block"
  },
  "post_install_review": {
    "$comment": "ClawHub displays this checklist on first install — investor must explicitly tick each item before the skill becomes active. Mirrors OpenClaw's user-consent screen for sensitive permissions.",
    "items": [
      {
        "id": "broker-required",
        "title": "muhaven-broker daemon must be running and authenticated.",
        "doc_ref": "https://github.com/hasToDev/muhaven/blob/master/packages/mcp/README.md"
      },
      {
        "id": "broker-bin-on-path",
        "title": "muhaven-broker binary reachable on $PATH. ClawHub install only resolves the skill bin into a runtime-local node_modules; the broker (shipped in @muhaven/mcp) may not be on $PATH unless installed separately via `npm install -g @muhaven/mcp`.",
        "doc_ref": "https://github.com/hasToDev/muhaven/blob/master/packages/openclaw-skill/SKILL.md#how-to-install"
      },
      {
        "id": "no-auto-submit",
        "title": "This skill never moves funds without your confirmation. Three-tier dispatch is enforced server-side."
      },
      {
        "id": "deny-default-egress",
        "title": "Network egress is allowlisted to MuHaven origins only. Any other endpoint is blocked at the runtime."
      },
      {
        "id": "no-secret-paste",
        "title": "JWT lives in your OS keychain via the broker. The skill cannot prompt you to paste a token."
      }
    ]
  },
  "$comment_unknown_field_handling": "Per OpenClaw issue #28298: unknown fields in this document MUST be treated as deny by the runtime. Never as allow. Forward-compat upgrades that introduce new permission types are gated by manifest.compatibility.openclaw_runtime.min."
}


path: dist/index.d.ts
content: import { ToolEntry } from '@muhaven/mcp';

/**
 * OpenClaw skill entry point. Bundles `@muhaven/mcp` with a hardcoded
 * tool subset per ADR-C: `read.*` (5) + Wave 4 P11 reads
 * (`read.protection_coverage`, `read.kyc_attestation`) +
 * `position.{buy,claim}` (2) + `policy.{pause,session_key_status}` (2)
 * = 11 tools. Eleven additional upstream tools (`position.{sell,
 * rebalance}` + `policy.{set_tier,audit_export}` + the five
 * `issuer.*` tools + the two `governance.*` tools) are deliberately
 * excluded — see `SKILL.md` `mcp.toolset_excluded_reason`.
 *
 * The skill does NOT mint its own MCP server — it imports `@muhaven/mcp`'s
 * `runMcpStdioCli` and supplies a `filterRegistry` callback that prunes
 * excluded tools. This keeps the descriptor SHA-256 hashes identical to
 * the upstream MCP package (post-MCPoison: hash drift in a bundled
 * subset would falsely trip the mcp-context-protector pin). Hash
 * verification fires inside `runMcpStdioCli` BEFORE the filter, so an
 * attacker who patches a single descriptor cannot hide the patch by
 * shipping a subset filter that excludes the patched tool — the patched
 * bytes still get hashed against the unfiltered `TOOL_DESCRIPTORS` array.
 *
 * The toolset_subset MUST stay in sync with `manifest.json`,
 * `manifest.json#tools`, and the SKILL.md frontmatter.
 * `scripts/verify-subset.ts` enforces this at build + CI gate time + via
 * a vitest at `__tests__/subset.test.ts` + `__tests__/manifest-consistency.test.ts`.
 */

/** Tool names exposed by this skill. ORDER-INDEPENDENT — used as a Set. */
declare const TOOLSET_SUBSET: readonly string[];
/** Tools deliberately excluded — see SKILL.md `mcp.toolset_excluded_reason`. */
declare const TOOLSET_EXCLUDED: readonly string[];
/**
 * Pure function — returns the OpenClaw-allowed subset of `registry`.
 * Throws if any tool listed in `TOOLSET_SUBSET` is missing from the live
 * registry (signals an upstream version drift that must be reconciled
 * before the skill can boot).
 *
 * Note: `--read-only` mode in the upstream package narrows `registry` to
 * `read.*` only BEFORE this filter runs; in that case the OpenClaw
 * subset shrinks accordingly (read tools only). That's the intended
 * behaviour — read-only is strictly subset of the read-only-allowed
 * portion of the OpenClaw subset.
 */
declare function selectOpenClawSubsetRegistry(registry: readonly ToolEntry[]): readonly ToolEntry[];
/** Boot the skill's MCP STDIO server. */
declare function runOpenClawSkill(): Promise<void>;

export { TOOLSET_EXCLUDED, TOOLSET_SUBSET, runOpenClawSkill, selectOpenClawSubsetRegistry };


path: LICENSE
content: MIT License

Copyright (c) 2026 hasToDev

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


path: manifest.json
content: {
  "$comment": "OpenClaw skill manifest for muhaven-rwa-skill. Mirrors the SKILL.md frontmatter in machine-readable form. ClawSecure permissions block is forward-compatible with NemoClaw runtime sandbox (OpenClaw issues #28298 / #28360). Network deny-by-default + filesystem deny-all + spawn deny-all is the lethal-trifecta defense (P8 forthcoming).",
  "schema_version": "1.0",
  "name": "muhaven-rwa-skill",
  "display_name": "MuHaven RWA Portfolio",
  "version": "0.1.4",
  "description": "Confidential RWA portfolio agent on Fhenix CoFHE. Reads encrypted balances, stages buy + claim intents, never auto-submits. Three-tier confirmation surface (inline / Mini App OTP / passkey deep-link) keyed on intent notional.",
  "license": "MIT",
  "homepage": "https://muhaven.app",
  "documentation": "https://github.com/hasToDev/muhaven/tree/master/packages/openclaw-skill",
  "support": "https://github.com/hasToDev/muhaven/issues",
  "keywords": [
    "fhe",
    "fhenix",
    "rwa",
    "muhaven",
    "openclaw",
    "confidential-defi",
    "erc-3643"
  ],
  "authors": [
    {
      "name": "MuHaven",
      "email": "[REDACTED_SECRET]",
      "url": "https://muhaven.app"
    }
  ],
  "compatibility": {
    "platforms": ["darwin", "win32", "linux"],
    "runtimes": {
      "node": ">=20"
    },
    "openclaw_runtime": {
      "$comment_calendar_versioning": "The npm-published `openclaw` package uses calendar versioning (e.g. 2026.5.7), not semver. The pre-2026-05-11 manifest assumed the NemoClaw-style semver range (0.4.0 / 0.5.x); that range is meaningless against the actual runtime. Range below pins the calendar major-minor at which §3 Path (a) A.4 was validated. Future-runtime adopters should bump `min` to the lowest calendar version that ships the `mcp.command/args` + `${__dirname}` template resolver the skill relies on.",
      "min": "2026.5.0",
      "tested": "2026.5.x"
    }
  },
  "runtime": {
    "type": "node",
    "entry_point": "dist/index.cjs",
    "args": []
  },
  "permissions": {
    "network": {
      "$comment_egress_advisory_in_host_native": "Allowlist intentionally pins production origins only — the published artifact is prod-targeted. Staging rehearsals (§3 Path (a) A.3-A.7 in AGENTIC_TEST_PLAN.md) install the same signed tarball with a `user_config.backend_url=https://api-stage.muhaven.app` override and rely on plain OpenClaw's `sandbox.fallback: host_native` posture (manifest permissions become advisory metadata, not kernel-enforced). Defense-in-depth in host_native mode lives in the bin shim's deny-default behavior + the broker daemon's JWT-scope check. A future kernel-enforced runtime would reject the staging override; that is intentional — production artifact stays production-pure.",
      "deny_default": true,
      "egress_allowlist": [
        "https://api.muhaven.app",
        "https://muhaven.app"
      ],
      "ingress_listen": []
    },
    "filesystem": {
      "read": [],
      "write": []
    },
    "process": {
      "spawn": [],
      "stdio": "manifest_only"
    },
    "secrets": {
      "storage": "os_keychain",
      "deny_paste_ui": true,
      "references": [
        {
          "name": "muhaven_jwt",
          "owner": "muhaven-broker",
          "scope": ["mcp.read.*", "mcp.propose.*"]
        }
      ]
    },
    "telemetry": {
      "deny_default": true
    }
  },
  "sandbox": {
    "runtime": "nemoclaw",
    "fallback": "host_native",
    "$comment_forward_compat": "Per OpenClaw issues #28298 (permissions schema) and #28360 (sandbox boot ABI). When the runtime doesn't recognize a field, it MUST treat unknown permissions as deny — not allow. Verified on NemoClaw 0.5.0 release notes.",
    "$comment_runtime_decision": "Operator runtime decision 2026-05-11 (c8aba6d) shifted §3 Path (a) A.4 from NemoClaw to plain OpenClaw via `npm install -g openclaw@latest`. The `runtime: nemoclaw` field above stays as the preferred runtime claim; `fallback: host_native` is what actually runs today. Sandbox + permissions become advisory in host_native mode. Promote to NemoClaw once the homelab is willing to host the NVIDIA OpenShell + GPU stack."
  },
  "mcp": {
    "bundled": "@muhaven/mcp",
    "bundled_version": "0.1.5",
    "transport": "stdio",
    "command": "node",
    "$comment_args_template_resolver": "`${__dirname}` is the MCPB-canonical template token (resolves to the skill's install-time root). Plain OpenClaw's manifest resolver behavior is empirically verified during §3 Path (a) A.4 (operator-driven). The bin shim is the launch target (NOT `dist/index.cjs` directly) because it wraps `runOpenClawSkill()` in `.then(...) .catch(err => process.stderr.write(\"fatal: \" + err.stack))` — direct-launch of `dist/index.cjs` would crash silently on an unhandled rejection. If a runtime fails to expand `${__dirname}`, the `runtime.entry_point: dist/index.cjs` field at manifest line 40 is the documented fallback hook.",
    "args": ["${__dirname}/bin/muhaven-rwa-skill.cjs"],
    "env": {
      "MUHAVEN_BACKEND_URL": "${user_config.backend_url}",
      "MUHAVEN_DASHBOARD_URL": "${user_config.dashboard_url}",
      "MUHAVEN_BROKER_ENDPOINT": "${user_config.broker_endpoint}",
      "MUHAVEN_READ_ONLY": "${user_config.read_only}",
      "$comment_skill_version_env": "MUHAVEN_OPENCLAW_SKILL_VERSION is intentionally NOT pre-set here. The bin shim's runOpenClawSkill() does `process.env.MUHAVEN_OPENCLAW_SKILL_VERSION = resolveSkillVersion()` at boot — the value comes from the `__SKILL_VERSION__` tsup-injected build-time constant (which itself reads `packages/openclaw-skill/package.json#version`). Hardcoding a literal here was the 5th drift site; the bin override always wins and a stale literal here only confuses install-time inspection."
    },
    "tool_subset": [
      "muhaven.read.portfolio",
      "muhaven.read.yields",
      "muhaven.read.distribution",
      "muhaven.read.tokens",
      "muhaven.read.audit",
      "muhaven.read.protection_coverage",
      "muhaven.read.kyc_attestation",
      "muhaven.position.buy",
      "muhaven.position.claim",
      "muhaven.policy.pause",
      "muhaven.policy.session_key_status"
    ],
    "tool_subset_excluded": [
      "muhaven.position.sell",
      "muhaven.position.rebalance",
      "muhaven.policy.set_tier",
      "muhaven.policy.audit_export",
      "muhaven.issuer.distribute_yield",
      "muhaven.issuer.kyc_add",
      "muhaven.issuer.kyc_remove",
      "muhaven.issuer.unpause_token",
      "muhaven.issuer.audit_query",
      "muhaven.governance.propose",
      "muhaven.governance.cast_vote"
    ]
  },
  "tools": [
    { "name": "muhaven.read.portfolio", "description": "Encrypted-balance portfolio summary; aggregates only.", "sensitive": false },
    { "name": "muhaven.read.yields", "description": "Per-token yield history (cleartext aggregates).", "sensitive": false },
    { "name": "muhaven.read.distribution", "description": "Distribution status for a (token, epoch).", "sensitive": false },
    { "name": "muhaven.read.tokens", "description": "RWA tokens the user holds.", "sensitive": false },
    { "name": "muhaven.read.audit", "description": "User's tiered-autonomy audit log.", "sensitive": false },
    { "name": "muhaven.read.protection_coverage", "description": "Read DefaultProtection coverage for an RWA token (reserveRateBps + status). Public aggregates only.", "sensitive": false },
    { "name": "muhaven.read.kyc_attestation", "description": "Read-only informational explainer for MuHaven cross-chain KYC attestations.", "sensitive": false },
    { "name": "muhaven.position.buy", "description": "Stage a Subscription buy — emits intent for three-tier confirmation. NEVER auto-submits.", "sensitive": true },
    { "name": "muhaven.position.claim", "description": "Stage a yield claim — emits intent for three-tier confirmation. NEVER auto-submits.", "sensitive": true },
    { "name": "muhaven.policy.pause", "description": "Activate /pause kill-switch (uninstallPlugin). NEVER auto-submits — requires user confirmation.", "sensitive": true },
    { "name": "muhaven.policy.session_key_status", "description": "Inspect ZeroDev session-key state.", "sensitive": false }
  ],
  "user_config": [
    {
      "key": "backend_url",
      "type": "string",
      "title": "MuHaven backend URL",
      "description": "Backend host. Default: https://api.muhaven.app (production).",
      "default": "https://api.muhaven.app",
      "sensitive": false
    },
    {
      "key": "dashboard_url",
      "type": "string",
      "title": "MuHaven dashboard URL",
      "description": "Dashboard origin used by deep-link confirmations and the device-code authorization page (/link). Hardcoded for phishing resistance — do not change unless you know what you are doing.",
      "default": "https://muhaven.app",
      "sensitive": false
    },
    {
      "key": "broker_endpoint",
      "type": "string",
      "title": "muhaven-broker IPC endpoint",
      "description": "Path to the broker's Unix socket (POSIX) or named pipe (Windows). Leave blank for the per-user default.",
      "default": "",
      "sensitive": false
    },
    {
      "key": "read_only",
      "type": "boolean",
      "title": "Read-only mode",
      "description": "When enabled, only the muhaven.read.* toolset is exposed; position.* and policy.* tools are not registered.",
      "default": false,
      "sensitive": false
    }
  ],
  "publishing": {
    "registry": "clawhub",
    "trusted_publishing": {
      "provider": "github",
      "repository": "hasToDev/muhaven",
      "workflow": ".github/workflows/openclaw-skill-publish.yml",
      "oidc_audience": "openclaw://clawhub-trusted-publish"
    },
    "signing": {
      "provider": "sigstore",
      "transparency_log": "rekor.sigstore.dev",
      "expected_artifact": "muhaven-rwa-skill-${version}.tgz",
      "expected_bundle": "muhaven-rwa-skill-${version}.tgz.sigstore"
    },
    "policy": {
      "required_reviewers": 2,
      "block_unsigned": true,
      "block_unscanned": true
    }
  },
  "scanning": {
    "openclaw_skills_publish_scan": true,
    "snyk_mcp_scan": true,
    "scan_artifact": "scan-report.json"
  }
}


path: package.json
content: {
  "name": "@muhaven/openclaw-skill",
  "version": "0.1.4",
  "description": "OpenClaw skill bundling a curated subset of @muhaven/mcp for the muhaven-rwa-skill ClawHub publish. Three-tier confirmation surface (Telegram inline / Mini App OTP / dashboard passkey deep-link).",
  "type": "module",
  "private": true,
  "main": "./dist/index.cjs",
  "module": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js",
      "require": "./dist/index.cjs"
    }
  },
  "bin": {
    "muhaven-rwa-skill": "./bin/muhaven-rwa-skill.cjs"
  },
  "files": [
    "dist",
    "bin",
    "manifest.json",
    "config.json",
    "SKILL.md",
    "README.md",
    "SECURITY.md",
    "CHANGELOG.md"
  ],
  "devDependencies": {
    "@types/node": "^22.0.0",
    "rimraf": "^5.0.0",
    "tsup": "^8.5.1",
    "tsx": "^4.21.0",
    "typescript": "^5.7.0",
    "vitest": "^3.0.0",
    "@muhaven/mcp": "0.1.5"
  },
  "engines": {
    "node": ">=20.0.0"
  },
  "keywords": [
    "openclaw",
    "claude-skill",
    "fhe",
    "fhenix",
    "rwa",
    "muhaven",
    "telegram-bot"
  ],
  "license": "MIT",
  "scripts": {
    "build": "tsup",
    "dev": "MUHAVEN_DEV_BUILD=1 tsup --watch",
    "clean": "rimraf dist",
    "typecheck": "tsc --noEmit",
    "test": "vitest run",
    "verify-subset": "tsx scripts/verify-subset.ts",
    "verify-versions": "tsx scripts/verify-versions.ts"
  }
}

path: README.md
content: # `@muhaven/openclaw-skill` — `muhaven-rwa-skill`

ClawHub-publishable OpenClaw skill that bundles a curated subset of
`@muhaven/mcp`. Hosted under the workspace name `@muhaven/openclaw-skill`
and published to ClawHub as `muhaven-rwa-skill`.

## Why a separate package?

The `@muhaven/mcp` package targets MCPB hosts (Claude Desktop, Cursor,
Claude Code) with all 22 tools across five groups (read / position /
policy / issuer / governance). The OpenClaw surface is investor-only:
read your portfolio + protection coverage + KYC attestation context,
stage a buy or claim, and pause the agent. The remaining tools
(`set_tier`, `audit_export`, `sell`, `rebalance`, the five issuer-side
tools, the two governance tools) are either dashboard-only ceremonies
or higher-blast-radius actions that need policy soak-time before going
live on a Telegram-driven surface.

ADR-C in `development/research-docs/WAVE_4_AGENTIC_RESEARCH_RESULT.md`
documents the architectural decision; `SKILL.md` carries the
operator-facing rationale.

## Boot sequence

1. OpenClaw runtime loads `manifest.json` + `config.json`.
2. `permissions.network.deny_default` + `permissions.filesystem.{read,write}: []`
   + `permissions.process.spawn: []` install before the skill binary
   starts. Any egress / FS / spawn attempt is denied at the runtime.
3. `bin/muhaven-rwa-skill.cjs` (CommonJS shim) calls `runOpenClawSkill()`.
4. `runOpenClawSkill()` delegates to `@muhaven/mcp`'s `runMcpStdioCli`
   with a `filterRegistry` callback that prunes excluded tools. The
   upstream's tool-description SHA-256 verification fires BEFORE the
   filter — so an attacker who patches a single descriptor cannot hide
   the patch by shipping a subset filter that excludes it.

## Tool subset (single source of truth)

The list lives in `src/index.ts` `TOOLSET_SUBSET`. `manifest.json` and
`SKILL.md` mirror it; `scripts/verify-subset.ts` runs at build time and
in CI to fail on drift.

| Tool | Group | Why included |
|---|---|---|
| `muhaven.read.portfolio` | read | Core "what do I own?" question |
| `muhaven.read.yields` | read | Per-token yield context |
| `muhaven.read.distribution` | read | Distribution-status answers |
| `muhaven.read.tokens` | read | Token list + symbols |
| `muhaven.read.audit` | read | "Why was I paused?" forensics |
| `muhaven.read.protection_coverage` | read | DefaultProtection reserve-rate aggregate (P11) |
| `muhaven.read.kyc_attestation` | read | KYC attestation explainer (P11) |
| `muhaven.position.buy` | position | Subscription buy intent |
| `muhaven.position.claim` | position | Yield claim intent |
| `muhaven.policy.pause` | policy | Kill-switch (cascade or per-surface) |
| `muhaven.policy.session_key_status` | policy | Read-only state inspection |

Excluded (11 total): `muhaven.position.sell`, `muhaven.position.rebalance`,
`muhaven.policy.set_tier`, `muhaven.policy.audit_export`, plus the five
`muhaven.issuer.*` tools (`distribute_yield`, `kyc_add`, `kyc_remove`,
`unpause_token`, `audit_query`) and the two P11 `muhaven.governance.*`
tools (`propose`, `cast_vote`). Each exclusion has a documented reason
in `manifest.json#mcp.tool_subset_excluded` and in `SKILL.md`
`mcp.toolset_excluded_reason`.

## Build + verify

```bash
pnpm --filter @muhaven/openclaw-skill build           # tsup ESM/CJS + DTS
pnpm --filter @muhaven/openclaw-skill verify-subset   # 9-way drift check (subset + excluded + tools[] + upstream + bundled_version triple-match)
pnpm --filter @muhaven/openclaw-skill typecheck       # tsc --noEmit
pnpm --filter @muhaven/openclaw-skill test            # vitest
```

## Three-tier confirmation contract

The skill itself never executes a state-mutating action. Tool calls
return an unsigned UserOp envelope plus a structured intent record;
the backend's `/api/v1/agent/openclaw/intent` endpoint mints a
confirmation record routed to one of three tiers based on amount.
See `backend/api/v1/agent/openclaw/*` for the routing and the
companion `telegram-bot/` worker for the Telegram surface.

## Hardening invariants

- `permissions.network.deny_default: true` — egress allowlist locked to
  the MuHaven backend + dashboard origins (`https://api.muhaven.app`,
  `https://muhaven.app`). Any new endpoint requires a manifest update +
  signed re-publish.
- `permissions.secrets.storage: os_keychain` — paste-token UX is forbidden.
- `permissions.process.spawn: []` — no shell, no Python, no JIT-compiled blob.
- Sigstore signing + GitHub OIDC trusted publishing — long-lived ClawHub
  tokens are not used.
- `required_reviewers: 2` — single-maintainer publish is rejected at the
  policy gate.

## Reference docs

- `development/research-docs/WAVE_4_AGENTIC_RESEARCH_RESULT.md` ADR-C
- `development/DEV_WAVE_4/TOOL_NAMESPACE.md`
- `development/DEV_WAVE_4/THREAT_MODEL_P0.md`
- `packages/mcp/README.md`

## License

MIT.


Security scan verdicts — ClawScan: suspicious | static analysis: suspicious | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)