Commit graph

1043 commits

Author SHA1 Message Date
rcourtman
e2ec59a840 Prepare v6.1.0 RC1 release
Some checks failed
Build and Test / Secret Scan (push) Waiting to run
Build and Test / Frontend & Backend (push) Waiting to run
Canonical Governance / governance (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 1/4) (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 2/4) (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 3/4) (push) Waiting to run
Core E2E Tests / E2E verdict (push) Blocked by required conditions
Unified Agent Native Verification / Linux ARM64 (push) Waiting to run
Unified Agent Native Verification / Linux x64 (push) Waiting to run
Unified Agent Native Verification / Windows x64 (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 4/4) (push) Waiting to run
Unified Agent Native Verification / macOS ARM64 (push) Waiting to run
Unified Agent Native Verification / macOS Intel (push) Waiting to run
Unified Agent Native Verification / FreeBSD cross-build contract (push) Waiting to run
Helm CI / Lint and Render Chart (push) Has been cancelled
2026-07-13 18:35:08 +01:00
rcourtman
1fd930e71d Document RG-06 autonomy proof runner 2026-07-13 18:32:56 +01:00
rcourtman
a393744894 Add in-app release highlights 2026-07-13 18:30:29 +01:00
rcourtman
4c3e0756e3 Guard the allowed_signers format in the install verification docs 2026-07-13 16:27:04 +01:00
rcourtman
4c073d6b17 Add mock action lifecycle data 2026-07-13 16:24:06 +01:00
rcourtman
f095da2fdb Wire production Proxmox action verification 2026-07-13 11:04:09 +01:00
rcourtman
8ffb56946e chore: seal Pulse Intelligence release gate 2026-07-13 01:14:19 +01:00
rcourtman
7d772acff3 feat(assistant): mid-turn steering of the running response
Some checks are pending
Core E2E Tests / Playwright Core E2E (shard 1/4) (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 2/4) (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 4/4) (push) Waiting to run
Core E2E Tests / E2E verdict (push) Blocked by required conditions
Unified Agent Native Verification / Linux ARM64 (push) Waiting to run
Unified Agent Native Verification / Linux x64 (push) Waiting to run
Unified Agent Native Verification / Windows x64 (push) Waiting to run
Build and Test / Secret Scan (push) Waiting to run
Build and Test / Frontend & Backend (push) Waiting to run
Canonical Governance / governance (push) Waiting to run
Helm CI / Lint and Render Chart (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 3/4) (push) Waiting to run
Unified Agent Native Verification / FreeBSD cross-build contract (push) Waiting to run
Unified Agent Native Verification / macOS ARM64 (push) Waiting to run
Unified Agent Native Verification / macOS Intel (push) Waiting to run
A follow-up sent during an active run now offers itself to the running
agentic loop via POST /api/ai/sessions/{id}/steer. Accepted steers join
the loop at its next turn boundary (the abort-check site) as plain user
messages, are announced with a steer_applied stream event so the drawer
settles the pending row, and persist through the end-of-run save. A
steer carries prompt text only: no route, control-level, or autonomy
changes, no turn-budget extension, system sessions rejected, and the
per-session inbox is bounded (steer_backlog overflow). Delivery is not
guaranteed by acceptance: a run that ends first discards the inbox and
the row drains as an ordinary queued turn, so pre-steering queue
semantics remain the fallback. Steering rows lose edit/remove once
accepted.
2026-07-12 23:01:40 +01:00
rcourtman
42fe61493c feat(assistant): show estimated session cost in the last-turn summary
Chat turns, compaction, and title calls now stamp session_id on their
cost.UsageEvent, and the done event carries session_cost_usd summed from
the operator ledger (cost.Store.SessionCostUSD). The drawer's last-turn
summary appends '$0.12 session' with a sub-cent floor. The figure is
omitted whenever any of the session's models has unknown pricing, and
free local models price known-at-zero, so no figure is ever partial.
2026-07-12 22:13:23 +01:00
rcourtman
99aa2bd6eb Add Colima autonomy release proof 2026-07-12 19:47:56 +01:00
rcourtman
2d47c12e6d Normalize Pulse Intelligence release gates 2026-07-12 14:42:07 +01:00
rcourtman
0062128414 Add durable Docker restart lifecycle proof 2026-07-12 12:11:16 +01:00
rcourtman
76f1084c9b Enforce server-owned Patrol Autopilot acknowledgement 2026-07-11 23:12:00 +01:00
rcourtman
5a6871813f Grant CAP_NET_RAW to the installed systemd unit for ICMP probes
The installer's unit hardens with NoNewPrivileges=true, which strips the
ping binary's setuid/file capabilities, so ICMP availability probes could
never work on a systemd install. Grant the capability ambiently instead,
document the systemctl edit override for existing units, and pin the
hardening block with an install test and contract clause.

Addresses #1554 (discussion)
2026-07-11 18:15:22 +01:00
rcourtman
4edf5f8265 fix: close chat command authority boundary 2026-07-11 11:54:56 +01:00
rcourtman
eb9954618a Add governed storage pressure cleanup 2026-07-11 10:11:35 +01:00
rcourtman
1312da3acb Add governed host update autonomy
Some checks are pending
Unified Agent Native Verification / Linux ARM64 (push) Waiting to run
Unified Agent Native Verification / Linux x64 (push) Waiting to run
Build and Test / Secret Scan (push) Waiting to run
Build and Test / Frontend & Backend (push) Waiting to run
Canonical Governance / governance (push) Waiting to run
Helm CI / Lint and Render Chart (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 1/4) (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 2/4) (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 3/4) (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 4/4) (push) Waiting to run
Core E2E Tests / E2E verdict (push) Blocked by required conditions
Unified Agent Native Verification / Windows x64 (push) Waiting to run
Unified Agent Native Verification / macOS ARM64 (push) Waiting to run
Unified Agent Native Verification / macOS Intel (push) Waiting to run
Unified Agent Native Verification / FreeBSD cross-build contract (push) Waiting to run
2026-07-11 01:25:14 +01:00
rcourtman
69b2028513 Add policy-scoped Patrol autonomy 2026-07-11 00:37:34 +01:00
rcourtman
d5437a9353 Allow RCs while Windows signing is pending 2026-07-10 23:12:12 +01:00
rcourtman
92ea590000 Prepare v6.0.6 RC1 release packet 2026-07-10 21:39:03 +01:00
rcourtman
5bbfce956d Complete Patrol action lifecycle continuity 2026-07-10 21:22:06 +01:00
rcourtman
3bea52b1b5 Harden native Windows agent lifecycle 2026-07-10 19:16:23 +01:00
rcourtman
67c2534c08 Replace the hard-coded tool classifier with registry-owned invocation descriptors
Every registered Pulse tool now carries a canonical invocation
descriptor (internal/agentcapabilities/invocation.go): static or
discriminator-based, classifying each invocation with a workflow kind
plus a mutation target (none / pulse_state / infrastructure). Mixed
descriptors must exactly cover their schema enum and registration
panics otherwise, so an unclassifiable tool cannot exist. Missing,
malformed, unknown, or fabricated discriminator values classify
fail-closed as infrastructure writes.

Provider projection and runtime enforcement consume the same
descriptor under one InvocationPolicy (control level plus the
request-local, non-serializable deny_infrastructure_mutations
restriction, isolated across executor clones): ListTools and
ListToolGovernance remove forbidden enum values, drop empty tools, and
recompute the offered action mode, while ToolRegistry.Execute blocks
forbidden invocations before the handler runs. This closes the mixed
tool control-level bypass, most seriously Docker action:update, which
previously fell through to direct execution at read-only, and fixes
the Kubernetes misclassification: the retired switch read the action
argument while the schema discriminator is type, so type:scale
classified as read.

pulse_file_edit is now write-only (append/write); file inspection
routes through pulse_read action=file, whose exec path keeps its
structural read-only execution-intent enforcement. ClassifyToolCall
consults the descriptor table first and retains only genuinely
non-registry compatibility cases. The deny restriction is deliberately
separate from autonomous mode, which only suppresses interactive
questions and grants no mutation authority.

Proofs: descriptor validation and fail-closed classification unit
tests, plus the invocation-policy regression suite (scale classifies
write and never invokes at read-only or under deny; Docker update
queues nothing at read-only; autonomous plus deny cannot mutate;
fabricated enum values fail at runtime; filtered projection and
runtime enforcement agree; executor clones keep request policies
isolated). Contracts and registry ownership updated for the new
shared invocation descriptor boundary.

Slice 3a of the typed-lifecycle ratchet; the patrol_investigation
execution profile and patrol_propose_action tool build on this
substrate next.
2026-07-10 13:31:11 +01:00
rcourtman
99dad2b511 Add a guided Ollama quickstart blessing qwen3:8b for Patrol
Ollama is the zero-cost AI path but the setup row offered only a Server
URL, and Patrol then failed on models that cannot emit tool_calls
(#1463, #847, #1425, #1152, #880). Bless qwen3:8b, the model family
Ollama's own tool-calling docs are written against, verified locally
against Patrol's real preflight: qwen3:8b emitted the tool call on
every run; qwen3:4b never did (0/4), so no low-RAM tag is suggested.

- Registry: SuggestedModel/Note/Equivalents on AIProviderDefinition,
  projected on /api/settings/ai providers; Ollama default model goes
  llama3.2 -> qwen3:8b.
- Provider row: copyable 'ollama pull qwen3:8b' block with hardware
  note, and a next-step hint when a successful test resolves a model
  outside the blessing set.
- Model resolution: exact-ID blessed preference, so pulling qwen3:8b
  makes it the auto-resolved Patrol model with no manual selection.
- Readiness copy names the blessed model (its contract pins landed
  with 94ccc7a0c's staging; this commit restores green).
- manual_ollama_preflight_test.go is the env-gated re-blessing
  harness; contracts updated for ai-runtime, api-contracts,
  frontend-primitives, and the agent-lifecycle/storage-recovery
  dependent boundaries; subsystem_lookup_test line pins follow the
  api-contracts.md insertion.
2026-07-10 13:02:26 +01:00
rcourtman
0e40ec07cb Make the provider MSP portal honest and self-sufficient without an email provider
Exercised the full provider portal as a pilot MSP would and fixed what
made it feel broken:

- The signed-out portal promised "a sign-in link is on the way" even when
  the control plane has no email provider (the bundle default), and team
  invitations silently sent nothing. The portal bootstrap now carries
  email_sign_in_available and provider_hosted_mode; the sign-in page shows
  the host command that actually prints a link, and the invite panel says
  invitation emails are not sent and how to hand over a link instead.
- New "provider-msp portal-link --email" CLI mints a one-time portal link
  for an account member or pending invitee, so teammates can sign in at
  all on email-less installs (bootstrap only covers the owner).
- Portal sessions were fixed at 12h; CP_SESSION_TTL now configures them
  and provider-hosted MSP mode defaults to 7 days.
- Creating a client past the license cap showed a generic "Failed to
  create workspace." toast: the limit error is now a JSON payload with
  current/limit, the API client no longer drops non-JSON error bodies
  (double body read), and the toast explains the license limit.
- Copy polish: provider-mode sign-in intro (no refunds/privacy register),
  least-privilege default invite role, queue tile label matches "Client
  onboarding", softer Support tab with a docs/MSP.md pointer, setup.sh
  summary now prints the bootstrap next step and day-2 sign-in commands,
  .env.example and docs/MSP.md document portal sign-in and sessions.

Contracts updated (cloud-paid, api-contracts, deployment-installability,
security-privacy) with verification pins in tenant_handlers_test,
config_test, magiclink_test, and provider_msp_deploy_test.

Verified live against a dockerless control plane: portal-link for an
invitee redeems, promotes the invitation, and sets a 7-day session;
the at-cap toast shows the license copy; portal vitest suite and
cloudcp/auth/account/installtests Go suites pass.
2026-07-10 11:22:57 +01:00
rcourtman
f356994869 Extract the transport-independent action lifecycle service
Planning, approval decisions, and execution for typed resource actions
move out of the HTTP handlers in internal/api/actions.go into a new
internal/actionlifecycle.Service owned by api-contracts. The REST
handlers become thin decode/actor/error-mapping adapters over the one
shared service, and ResourceHandlers.ActionLifecycle() exposes the same
service for in-process consumers, so a future Patrol action broker
inherits identical resource lookup, availability checks, plan hashing,
audit persistence, remediation locks, plan-drift revalidation,
execution, and terminal publication instead of loopback HTTP or a
parallel lifecycle.

Behavior is preserved: same status codes, error codes, and audit/
lifecycle persistence ordering, backed by the existing api contract
tests plus new fail-closed proofs for the service itself (unknown
resource/capability, availability refusal, unapproved execution,
remediation lock, plan drift, missing executor, missing store).

Contract text in api-contracts, agent-lifecycle, and storage-recovery
now names the service alongside actions.go and planner.go; the
subsystem registry owns internal/actionlifecycle/ under api-contracts
with a dedicated path policy; the code-standards and contract source
pins follow the moved invariants; and the subsystem_lookup line-number
pin shifts with the api-contracts canonical-files list insertion.

This is the first slice of making the typed action lifecycle the only
autonomous execution route for Patrol, Assistant, and MCP.
2026-07-10 11:06:55 +01:00
rcourtman
11bf0c9e74 Fix native agent release lifecycle verification 2026-07-10 01:49:38 +01:00
rcourtman
33d318a29e Delete the unreachable adapter Execute/Rollback update path and the unused UpdateQueue
The only production consumer of the UpdaterRegistry is the plan endpoint
(GET /api/updates/plan); every real apply runs through the in-Go pipeline
in manager.go ApplyUpdate, with concurrency held by updateMu/updateInFlight.
The InstallShAdapter Execute/Rollback path (install.sh piping, rollback
binary download, health wait) and the UpdateQueue were never called outside
tests, so fixes made there silently did nothing. Adapters are now plan
providers only: the Updater interface keeps SupportsApply, PrepareUpdate,
and GetDeploymentType, pinned by the new registry proof in
internal/api/updates_test.go.

The orphaned fetchAndVerifyReleaseSignature helper goes with it; its
fail-closed sidecar tests now exercise the manager's
downloadAndVerifyReleaseSignature directly. The deployment-installability
sshsig invariant now names the manager pipeline as the only surface that
fetches release artifacts, and api-contracts, storage-recovery, and
agent-lifecycle pin the same plan-provider-only shape at their update
transport boundaries. Stale adapter references in build-release.sh,
validate-release.sh, and the installtests comments are updated to match.
2026-07-10 01:14:40 +01:00
rcourtman
a3ef1226b7 Fail fast on missing native signing configuration 2026-07-09 23:44:22 +01:00
rcourtman
51c60df3a4 Exercise native signing in release rehearsals 2026-07-09 23:36:06 +01:00
rcourtman
453b478ac9 Validate release archives in one pass 2026-07-09 23:33:22 +01:00
rcourtman
255c7c23d4 Modernize Unified Agent lifecycle and platform support 2026-07-09 23:20:35 +01:00
rcourtman
a5b8d9a3ee Allow complete release candidate validation 2026-07-09 23:11:38 +01:00
rcourtman
cc0952e491 Run signed candidate builds during dispatched rehearsals 2026-07-09 22:32:40 +01:00
rcourtman
8dda0b6efa Build releases once and promote verified candidates 2026-07-09 22:21:34 +01:00
rcourtman
0fa841f66f Improve monitor-first attention states 2026-07-09 21:29:45 +01:00
rcourtman
7897a9131e Fix FreeBSD agent update recovery
Refs #1546
2026-07-09 20:36:58 +01:00
rcourtman
f8bbae2f34 Expose demo tailnet identity diagnostics 2026-07-09 20:32:43 +01:00
rcourtman
910418c3b2 Make stable patch releases unattended 2026-07-09 20:16:13 +01:00
rcourtman
2be167331d Harden demo SSH setup for IP targets 2026-07-09 17:06:52 +01:00
rcourtman
76ced45c3a Harden demo SSH setup for private deploy hosts
Some checks are pending
Build and Test / Secret Scan (push) Waiting to run
Build and Test / Frontend & Backend (push) Waiting to run
Canonical Governance / governance (push) Waiting to run
Helm CI / Lint and Render Chart (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 1/4) (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 2/4) (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 3/4) (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 4/4) (push) Waiting to run
Core E2E Tests / E2E verdict (push) Blocked by required conditions
2026-07-09 16:51:38 +01:00
rcourtman
1a05c715ac Reuse authenticated state in multi-tenant release E2E 2026-07-09 13:40:35 +01:00
rcourtman
fbddd91fcb Add install-script regression tests for the piped and version fixes
Cover the two installer bugs fixed in c98acb9e8 so they cannot regress:

- TestRootInstallScriptSourceGuardSurvivesPipedExecution runs the source guard
  through stdin the way curl ... | bash does and asserts it reaches the
  installer body instead of aborting on BASH_SOURCE[0] unbound. (#1526)

- TestInstallSHAgentVersionMismatchIgnoresVPrefix drives the version check and
  asserts v6.0.4 vs 6.0.4 does not warn while a real 6.0.3 vs 6.0.4 does, plus
  content assertions pinning the normalization in scripts/install.sh. (#1527)

Refs #1526 #1527
2026-07-09 12:04:41 +01:00
rcourtman
6999ca0119 Prepare 6.0.5 release 2026-07-09 11:52:28 +01:00
rcourtman
c98acb9e85 Harden the installers against piped execution and v-prefixed versions
Two installer bugs reported after the v5 to v6 upgrades:

- The server installer (install.sh) guarded its "am I being sourced?" check
  with a bare ${BASH_SOURCE[0]}. When piped to bash (curl ... | bash) there is
  no source file, so under `set -u` the run aborted with "BASH_SOURCE[0]:
  unbound variable" before the installer body. Default the lookup and only
  early-return on a genuine source. Regression of the v5 fix in #1396. (#1526)

- The agent installer (scripts/install.sh) compared the downloaded binary's
  version ("v6.0.4") against the server /api/version value ("6.0.4") verbatim,
  raising a spurious mismatch warning on matching versions. Strip a leading "v"
  from both sides before comparing so only a genuine difference warns. (#1527)

Refs #1526 #1527
2026-07-09 11:31:43 +01:00
rcourtman
93bccaff18 Update the Pro binary in-app from the license server download broker
Some checks are pending
Build and Test / Secret Scan (push) Waiting to run
Build and Test / Frontend & Backend (push) Waiting to run
Canonical Governance / governance (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 1/4) (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 2/4) (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 3/4) (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 4/4) (push) Waiting to run
Core E2E Tests / E2E verdict (push) Blocked by required conditions
The in-app updater and the unattended timer both target the public
rcourtman/Pulse community assets, so a Pro install that used them was
silently downgraded to community. Guard 2 (983a89326) blocked in-app
apply on the Pro edition, which stopped the downgrade but left Pro
installs with no update path except manual portal downloads. That
friction is a plausible driver of the runtime split: as of 2026-07-08
only 10 of 66 active paid licenses have any Pro-runtime install.

Root fix: the compiled Pro binary now checks and applies updates
through the license server download broker (GET /v1/downloads/pulse-pro
with the installation token and instance fingerprint). The check
compares against the broker's pinned private release instead of GitHub,
respecting the stable/rc channel guard. Apply re-resolves fresh signed
R2 URLs at apply time, verifies the archive against the same pinned
pulse-installer SSHSIG key plus the broker manifest sha256, and refuses
GitHub-shaped download URLs outright. An unactivated Pro binary still
refuses with the portal fallback. The community edition path is
unchanged.

The update banner restores in-app apply for auto-updatable Pro
deployments and keeps the portal instructions for deployments the
updater cannot drive (Docker). scripts/pulse-auto-update.sh now skips
when the installed binary reports Pulse Pro so the unattended timer can
never reinstall community over Pro.

Note: internal/updates/pro_update.go and manager_pro_update_test.go for
this change landed one commit early inside 313552deb via a parallel
session committing a shared staged index; this commit completes the
wiring they belong to.
2026-07-08 23:39:37 +01:00
rcourtman
313552debc Pin the OAuth Tailscale path in the update-demo workflow test
TestUpdateDemoWorkflowUsesGovernedNetworkPath still asserted the retired
authkey: TS_AUTHKEY line; 0a9a29d63 moved update-demo-server.yml to the
OAuth client (TS_OAUTH_CLIENT_ID / TS_OAUTH_SECRET, tag:infra), so the
test now pins those lines instead.
2026-07-08 23:23:20 +01:00
rcourtman
a1c22bf55c Re-pin the demo tailnet join on the OAuth client secrets
Commit 0a9a29d63 moved deploy-demo-server.yml and update-demo-server.yml
from the static TS_AUTHKEY to the Tailscale OAuth client but left the
release promotion policy pin asserting TS_AUTHKEY, so the Canonical
Governance run failed at the promotion policy unit tests once the
completion guard false positive (fixed in 54a6118d1) stopped masking
the step. The pin now asserts the OAuth client id and secret and pins
the static key retirement.
2026-07-08 14:40:17 +01:00
rcourtman
54a6118d17 Fix CI false positives in the canonical completion guard
The guard judged substantive contract updates by diffing HEAD against
the index. In CI nothing is staged, the index equals HEAD, so every
contract file piped in via --files-from-stdin looked unchanged and the
guard blocked compliant pushes. Concretely, run 28944317805 blocked
7645965af even though its deployment-installability.md addition sits
inside the Current State section.

The guard now accepts --diff-base <ref> (requires --files-from-stdin),
resolves it to its merge base with HEAD so the comparison anchor
matches the three-dot changed-file list, and compares base vs HEAD
contract texts in that mode. Pre-commit keeps the index comparison.
The canonical-governance workflow passes the push or PR range base.
2026-07-08 14:35:07 +01:00
rcourtman
7645965afe Derive the rollback target for scheduled release rehearsals
The weekly release-dry-run schedule failed at 'Resolve rehearsal
metadata' because GitHub does not apply workflow_dispatch input
defaults to schedule events, so rollback_version arrived empty and
resolve_release_promotion.py rejected the run.

Scheduled runs now pass --derive-rollback-latest-stable, which fills
an empty rollback_version with the latest stable tag preceding the
rehearsal version (currently v6.0.4 for 6.0.5-rc.3). Manual dispatches
keep the explicit rollback_version requirement; the stale prefilled
5.1.29 default is removed so operators state the target themselves.
The deployment-installability contract records the scoped scheduled
exception.
2026-07-08 13:52:48 +01:00