Commit graph

232 commits

Author SHA1 Message Date
rcourtman
faefe6edc8 Remove 198 unreachable Go functions
Dead-code sweep. Functions flagged unreachable by golang.org/x/tools/cmd/deadcode
and confirmed unused across pulse, pulse-enterprise, pulse-pro and pulse-mobile by
adversarial cross-repo verification. Cross-module reachability was checked
explicitly (only pkg/ exported symbols are importable by other modules; internal/
packages and _test.go files are not). go build, go vet and test-compile all pass.
2026-06-03 12:29:37 +01:00
rcourtman
1afd708e5a Harden unified agent update preflight
Record the single pulse-agent product invariant and clarify Docker / Podman module terminology.
2026-06-02 23:54:48 +01:00
rcourtman
d729461cad Harden provider MSP tenant isolation
- isolate provider MSP tenant runtimes onto per-client Docker networks
- add adversarial proofs for workspace cap races, handoff retargeting, and org-bound agent report retargeting
- pin provider deploy proof to start Traefik before tenant network creation
2026-06-02 17:34:10 +01:00
rcourtman
473a0ddee6 Add provider MSP tenant rollout upgrade proof 2026-06-02 16:45:03 +01:00
rcourtman
e86143f07f Surface provider MSP backup readiness 2026-06-02 16:02:04 +01:00
rcourtman
7e21263947 Add provider MSP install proof artifact test 2026-06-02 14:56:55 +01:00
rcourtman
4c31fa88f3 Add provider MSP install proof 2026-06-02 14:50:36 +01:00
rcourtman
e5a7300aff Add provider MSP workspace recovery 2026-06-02 14:28:16 +01:00
rcourtman
a2e860dc8c Add provider MSP backup restore 2026-06-02 14:14:07 +01:00
rcourtman
c9f84c5192 Add provider MSP backup command 2026-06-02 14:03:19 +01:00
rcourtman
e46f4d648e Prove provider MSP token rotation 2026-06-02 13:41:37 +01:00
rcourtman
f128c0b8cd Add provider MSP status command 2026-06-02 13:33:27 +01:00
rcourtman
c9c415d7a6 Require license-backed provider MSP proof 2026-06-02 13:22:15 +01:00
rcourtman
3630ab1867 Verify provider MSP agent report ingest 2026-06-02 13:14:16 +01:00
rcourtman
e99ca62c81 Add provider MSP preflight readiness checks 2026-06-02 13:02:13 +01:00
rcourtman
3f20c85e86 Add provider MSP proof command
Adds a provider-hosted MSP proof command that exercises provider bootstrap, workspace creation, hosted tenant install-token generation, handoff exchange, setup-facts visibility, and cross-tenant token isolation.
2026-06-02 12:45:48 +01:00
rcourtman
5134e36c28 Add provider-hosted MSP install path 2026-06-02 12:11:35 +01:00
rcourtman
aa4a5fa631 Add agent provisioning capabilities 2026-05-28 16:31:19 +01:00
rcourtman
84c43ad3e1 Fix Windows agent installer readiness path 2026-05-26 10:19:56 +01:00
rcourtman
5a405f7f07 Protect inside-guest Docker visibility boundaries 2026-05-18 14:16:15 +01:00
rcourtman
895b78bd89 Make Assistant tool choice model-owned 2026-05-15 11:27:15 +01:00
rcourtman
348582df66 Fix Assistant chat model-owned routing 2026-05-15 10:50:23 +01:00
rcourtman
f024d3b560 Align action audit verification projection 2026-05-13 18:36:00 +01:00
rcourtman
3da835c5bc Publish a distribution path for pulse-mcp
The MCP adapter shipped in slice 51 with one install option:
clone the repo and go build. This slice integrates pulse-mcp
into Pulse's existing governed release pipeline so a Pulse
release publishes a pulse-mcp binary alongside the unified agent
and the install scripts that bring it home in one command.

What ships:

  - scripts/build-release.sh extended to build pulse-mcp for
    the same multi-OS matrix as the unified agent, package
    per-platform tarballs and zips, and copy bare binaries to
    RELEASE_DIR for /releases/latest/download/ redirect
    compatibility.
  - .github/workflows/create-release.yml extended to upload
    the bare pulse-mcp binaries plus install-mcp.sh and
    install-mcp.ps1 as release assets.
  - scripts/install-mcp.sh: bash one-line installer that
    detects platform/arch, downloads the matching binary from
    the configured release (latest by default), verifies SHA256
    against the published checksums.txt, places at
    ~/.local/bin/pulse-mcp (or /usr/local/bin if not writable).
    Honors PULSE_MCP_VERSION, PULSE_MCP_BIN_DIR, PULSE_MCP_REPO,
    PULSE_MCP_NO_VERIFY env vars; declines Windows shells with
    a pointer at the .ps1 sibling.
  - scripts/install-mcp.ps1: PowerShell installer for Windows,
    placing pulse-mcp.exe at $LOCALAPPDATA\pulse-mcp.

Documentation aligned:

  - cmd/pulse-mcp/README.md gains an Install section above
    Quick start with three options: one-line installer,
    GitHub Release download, go install. Documents the macOS
    Gatekeeper bypass since v1 is unnotarized by design.
  - The Settings -> API Access agent-integrations panel now
    surfaces the curl|bash command above the config snippet so
    operators see "install pulse-mcp" before "configure your
    MCP client."
  - docs/releases/AGENT_PARADIGM.md drops the "no published
    distribution path" item from "what it does not do yet" and
    documents the Gatekeeper / Homebrew gaps as next-tier
    follow-ups.

Trade-offs surfaced and chosen:

  - Same cadence as Pulse: pulse-mcp ships per Pulse release,
    not on its own track. The MCP server reads the manifest
    from the Pulse it talks to, so version alignment is the
    natural model.
  - No Homebrew tap or core formula in v1. Maintaining a tap
    is real ongoing work; foundation supports adding Homebrew
    later as a layer.
  - No Docker image. Stdio JSON-RPC fights Docker's stdin
    /stdout pattern.
  - No notarization in v1. SHA256 verification through the
    installer preserves the audit trail; README documents the
    Gatekeeper bypass.

Subsystem contract: deployment-installability.md gains
scripts/install-mcp.sh, scripts/install-mcp.ps1, and
cmd/pulse-mcp/ in canonical files (mid-list entries
renumbered) plus a paragraph documenting the new MCP entry
point alongside the existing installer family.

Verification artifacts:

  - scripts/installtests/build_release_assets_test.go gains
    TestBuildReleasePackagesPulseMcpForAllPlatforms which pins
    the build/package/copy wiring and the load-bearing
    install-mcp.sh helpers (platform detection, SHA256
    verification, install-dir resolution).
  - scripts/release_control/render_release_body_test.py gains
    test_agent_paradigm_release_notes_blurb_documents_-
    distribution_path which pins the AGENT_PARADIGM.md draft's
    install-mcp.sh reference and the four-axis frame so a
    future edit cannot regress the install story silently.

Smoke-tested install-mcp.sh locally on darwin-arm64: platform
detection, install-dir resolution, URL building, and 404 error
handling all correct. The full end-to-end install path becomes
live the moment a Pulse release ships pulse-mcp binaries; the
next RC cut will exercise it.
2026-05-10 17:04:49 +01:00
rcourtman
76832797f4 Pin the MCP adapter's write path through tools/call
Slice 51's existing tools/call test only covered GET (no body),
so the body-argument extraction, JSON marshaling, Content-Type
header, and path-placeholder substitution working together for
PUT/POST capabilities went untested. If an agent calls
set_operator_state via MCP and the body doesn't make it through
correctly, the substrate would silently swallow the agent's
data and emit a "successful" response.

Three new tests fill the gap:

  - SendsPutBodyForWriteCapabilities exercises the canonical
    write path against a fake Pulse: PUT method, substituted path,
    bearer token header, application/json Content-Type, the body
    fields round-tripping through JSON marshaling, the upstream
    response surfacing in the MCP content block with isError=false,
    and server-populated attribution (setBy) reaching the agent.

  - TopLevelArgsMakeUpRequestBody pins the flexibility on the
    body argument: agents that pass body fields at the top level
    of arguments (no nested "body" key) get them collected into
    the upstream request body. This is the shape MCP clients tend
    to generate when they read the input schema as "object with
    these fields"; the bridge accepts both.

  - TopLevelArgsExcludesPathPlaceholders pins the disambiguation
    rule for the case both shapes overlap: when arguments include
    both a path placeholder and body fields at the top level,
    the placeholder goes ONLY in the URL, never duplicated into
    the body. Drift here would let canonicalId leak into a PUT
    body the server doesn't expect, which currently doesn't
    break Pulse but would break any future server that validates
    body fields against a stricter schema.

Test-only addition; no production code changed.
2026-05-10 14:45:04 +01:00
rcourtman
818221b457 Translate Pulse SSE events into MCP notifications when opted in
Closes the documented limitation in slice 51's pulse-mcp: MCP
clients that process server-initiated notifications can now
react to Pulse's push channel without holding a separate HTTP
connection to /api/agent/events.

The bridge is opt-in via --emit-notifications because not every
MCP client surfaces arbitrary notifications/* methods (Claude
Desktop, today, does not). Autonomous agents that consume the
JSON-RPC stream programmatically benefit; UI-mediated clients
should keep the flag off and use the SSE stream directly.

Implementation: a long-lived goroutine, started once after the
first initialize, that opens /api/agent/events, parses the
substrate's wire format, and emits a JSON-RPC notification
per non-transport event. Method names mirror the SSE event
kinds (notifications/finding.created, notifications/approval.
pending, notifications/action.completed). Params is the SSE data
payload verbatim so agents see the same wire shape an HTTP SSE
consumer would. stream.connected and heartbeat are filtered as
transport plumbing. The consumer reconnects with capped
exponential backoff on transient errors.

When --emit-notifications is on, initialize advertises the
supported event kinds under
capabilities.experimental.pulseNotifications.kinds. Clients that
don't understand the experimental block ignore it silently.

Three tests pin the behaviour: the initialize handshake's
capability block is correctly gated on the flag; the notification
filter rejects transport events and accepts the three substrate
kinds; an httptest.NewServer-backed end-to-end translates a
multi-event SSE stream into JSON-RPC notifications with the
substrate's payload preserved.

Also flagged in AGENT_SUBSTRATE.md "what it does not do yet": the
action-execution endpoints (/api/actions/plan, decision, execute)
emit a different error envelope from the agent surface (APIError
with stable code under "code") versus the agent-stable shape
(stable code under "error"). Adding them to the manifest
requires resolving that mismatch first; recorded as a focused
slice for whenever the substrate's reach extends to direct
agent-driven execution.
2026-05-10 14:19:44 +01:00
rcourtman
3a502fefda Publish the cmd/pulse-mcp integration guide
Slice 51 added the MCP adapter as a worked example. This makes it
a published surface: an external maintainer who wants to wire
Pulse into their Claude Desktop or Claude Code can read one
README and have it working without spelunking through main.go.

The guide carries:
- Build and install instructions
- Canonical config snippets for Claude Desktop and Claude Code
- The env-var contract (PULSE_API_TOKEN, configurable name,
  always read from env so it stays out of process listings)
- The published tool list grouped by category (context,
  operator-state, finding) with what each does
- The stable error envelope shape and the difference between
  capability-specific codes and cross-cutting auth codes
- Documented limitations: no subscribe_events, manifest fetched
  once, tools-only (no resource URIs)
- Troubleshooting for the common failure modes (missing token,
  proxy gating discovery, missing write scope)

api-contracts.md now points readers at the README as the
canonical integration entry point so the contract doc keeps
its in-repo focus and the README owns the user-facing copy.
2026-05-09 23:11:54 +01:00
rcourtman
d6a68f8044 Add cmd/pulse-mcp — MCP adapter wrapping the agent substrate
The whole point of slice 39's hand-authored manifest with
snake_case names and stable error codes was to make adapter
projection cheap. This slice is the test: a minimal MCP (Model
Context Protocol) server that turns Pulse's manifest into a tool
surface Claude Desktop, Claude Code, and other MCP-speaking
clients can drive natively.

Every MCP tool is a one-line projection of a manifest capability.
Input schemas are auto-derived from path placeholders ({name}
segments become required string properties) and method (non-
GET/DELETE tools accept a free-form body object). Adding a
capability to the manifest automatically extends the tool surface
— no MCP-side changes required.

The adapter is stdlib-only, runs over stdio with line-delimited
JSON-RPC 2.0 framing, preserves Pulse's stable error envelope
verbatim through MCP's content-and-isError result so agents on
the MCP side branch on the same codes they would on the wire,
and skips subscribe_events (SSE streaming doesn't fit the
request/response tool shape; future slices can layer it as MCP
notifications).

Eleven tests pin the projection rules and the JSON-RPC contract:
path-placeholder schema generation, body-property method gating,
substitution failures producing stable errors, the initialize
handshake advertising tools, tools/list filtering subscribe_events,
tools/call proxying with the bearer token and preserving the
substrate's error envelope, unknown methods producing JSON-RPC
method-not-found, and notifications producing no response.

The substrate is now wrapped in two adapters, each demonstrating a
different consumer profile: agent-probe walks the substrate as an
HTTP client (slice 49); pulse-mcp wraps it for stdio MCP clients.
Both depend only on the standard library and resolve paths from
the manifest, so the substrate is the single source of truth and
adapter additions stay cheap.
2026-05-09 22:51:37 +01:00
rcourtman
956646a5c1 Add cmd/agent-probe — worked example consuming the agent substrate
The substrate's read and write surfaces are end-to-end-tested
internally; this slice answers the harder question — "is the
substrate actually usable from the outside?" — by writing the
smallest standalone program that consumes it. agent-probe walks
the discovery → triage → depth → push flow against a running
Pulse instance using only the Go standard library, so it doubles
as a reference implementation for anyone building MCP servers,
Claude Code integrations, or custom agents on top of Pulse.

It resolves every path from the manifest rather than hardcoding
them — if discovery moves a path, the probe follows
automatically — and branches on the stable error envelope's
"error" code field, never on human-readable messages. The focus
rule (severity-lex-ordered) is intentionally simple so a reader
can predict what the probe will pick; real agents will have
richer policies.

This is documentation as code: the program is short enough to
read top-to-bottom and reads like the agent's own narration of
what it's doing. The unit test pins the focus rule's lex
ordering so a refactor that swaps it for a weighted score (which
allowed many warnings to outrank one critical) cannot regress
silently.
2026-05-09 22:28:00 +01:00
rcourtman
cf103ca9fe Harden root agent service defaults 2026-05-05 13:03:13 +01:00
rcourtman
a8428da1ea Add agent ID file persistence
Refs #1447
2026-04-30 14:41:57 +01:00
rcourtman
5091affb1b Canonicalize Docker Podman agent CLI copy 2026-04-30 09:04:25 +01:00
rcourtman
ecf8fd4299 Keep self-hosted Pro prompts opt-in 2026-04-28 11:23:49 +01:00
rcourtman
8e0fdd090c Record iOS mobile readiness proof 2026-04-26 12:10:16 +01:00
rcourtman
badda3781c Harden hosted mobile proof setup 2026-04-24 13:13:36 +01:00
rcourtman
5fd6456302 Tighten Pulse Cloud residue audit 2026-04-24 12:22:13 +01:00
rcourtman
8fa98ad0a3 Add Pulse Cloud storage guardrails 2026-04-24 11:57:23 +01:00
rcourtman
9dbaaa7efe Recover missing hosted tenant runtimes 2026-04-24 00:09:52 +01:00
rcourtman
c51708000f Tighten unified agent hardening proof 2026-04-23 23:37:25 +01:00
rcourtman
9bada35337 Harden unified agent runtime and installer 2026-04-23 23:04:18 +01:00
rcourtman
b33e21e0e8 Add least-privilege SSH deploy mode 2026-04-22 15:23:02 +01:00
rcourtman
21950c6e4c Restore QNAP agent boot and update continuity
Refs #1420

Refs #1422
2026-04-22 10:48:43 +01:00
rcourtman
7b1520b760 Add fingerprint-pinned TLS mode for unified agent 2026-04-22 01:36:46 +01:00
rcourtman
d0a60e1a92 Fix pulse-agent version exit handling
Refs #1424
2026-04-16 11:54:47 +01:00
rcourtman
b577164a1d Add hosted tenant runtime contract reconcile command 2026-04-04 09:24:42 +01:00
rcourtman
9ab2f8ab21 Add canonical hosted tenant runtime rollout control 2026-04-03 23:32:11 +01:00
rcourtman
2fdf0e16c8 Support enterprise mobile approval proof backend 2026-03-29 21:52:18 +01:00
rcourtman
778a2577b6 feat: Pulse v6 release 2026-03-18 16:06:30 +00:00
rcourtman
adad2bbd1c fix(server): fail fast on frontend bind errors 2026-03-03 15:43:22 +00:00
rcourtman
26776b2075 fix(agent): apply --disk-exclude to Docker agent disk metrics (#1237)
The Docker agent was not passing the disk exclusion list to
hostmetricsCollect(), so excluded mounts appeared in the Docker tab
disk totals. Also add server-side fsfilters filtering to Docker
report processing for parity with the host agent path.
2026-02-10 16:59:35 +00:00