10 Commits
Author SHA1 Message Date
João Carlos Magalhães 4fcca7c8c8 feat(opencode2): bind the plugin to the 2.0.10+ event api with turn checkpoints
OpenCode 2.0.10 replaced the lifecycle events the generated plugin listened
for (session.idle and friends) with session.execution.{succeeded,failed,
interrupted}, and it runs one long-lived service behind every CLI: closing a
terminal is not a session end, so sessions captured through the old binding
rarely produced a summary page or a baton. The plugin also re-ran two `git`
processes per event, lost startup context after the first model request,
dropped content-only tool results, and shared queue and spool state across
the per-location instances OpenCode 2 loads.

Plugin (install_hooks.rs, render_shared.rs, the node host fixture):
- binds session.execution.*, session.text.ended, session.moved,
  session.deleted, session.compaction.started and the context/prompt/tool
  hooks; every name was checked against the OpenCode 2.0.14 binary;
- claims startup context once per root session and re-injects it on every
  model request; child sessions never claim it or publish a baton;
- keeps queue, spool and cleanup state per location instance; spool names
  can no longer collide within a millisecond, and a torn-down host cancels
  what is still in flight only after its final session-ends had the drain
  budget (all generated TypeScript integrations share this runtime now);
- opt-in assistant capture (--capture-assistant) hands the last completed
  text to the native hook, which sanitizes and caps it before the spool or
  the wire, and falls back to the plain stop hook if that binary cannot run.

Server (router.rs, ops.rs, reader.rs, writer.rs):
- a completed root turn is a turn checkpoint: sessions/<id>.md and the
  automatic baton are refreshed deterministically (no LLM) in the session
  row's own scope, keeping one open baton per live session, refreshed in
  place and audited; a checkpoint that lost the race with the session's end
  touches nothing;
- an explicit, keyed session.moved rebinds the live session row to its new
  directory on first delivery (compare-and-set on the cwd it left), so the
  session's end and checkpoints follow it;
- a SessionEnd whose resolved scope drifted under the same cwd (a
  .ai-memory.toml appeared mid-session) ends the session instead of
  stranding it open; scope-drifted ordinary events are recorded as upstream
  records any other drifted event;
- the latest captured assistant excerpt rides in the automatic baton; it is
  not rendered into the git-tracked session page.

Docs: install.md, support-matrix.md, auto-scope.md, SECURITY.md and
DATA_HANDLING.md describe the checkpoint, routing and capture behavior.

Verified: cargo fmt --all -- --check; cargo clippy --workspace --all-targets
-- -D warnings; cargo test --workspace --all-targets (3569 passed, on release/2.5); the node
host fixture runs under cargo test (node >= 22.6) and fails if unload aborts
deliveries before its session-ends. Live on Windows 11 with OpenCode 2.0.14:
after a 64 -> 66 store migration, the running OpenCode service loaded the
regenerated plugin and one real turn through it (a Code Mode call to memory_status)
recorded its prompt, tool events and stop, logged "turn checkpoint written;
native session remains open", left the session open, wrote
sessions/<id>.md and exactly one open baton carrying the captured
assistant excerpt, which the session page does not contain.
2026-09-23 19:52:22 -03:00
Murilo PereiraandClaude Sonnet 5 9605bcb5b7 docs: add EU/enterprise-readiness reference docs (data handling, SSO, air-gapped install)
Consolidates facts already documented in SECURITY.md, docs/install.md, and
docs/local-embeddings.md into the shape a security/legal reviewer typically
needs before approving a dev tool: a single data-flow page, an SSO/OIDC
summary, and an offline-install path. No behavior change. Adds a fallback
security-contact path for reporters who can't use GitHub's private-advisory
flow.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-16 17:02:48 +02:00
AkitaOnRailsandClaude Opus 5 b3d1c222d5 fix(serve): warn instead of refusing an unauthenticated bind in a container
The v1.27.0 bind guard reads a non-loopback bind as evidence of network
exposure and refuses without a token. That inference holds on a host but
not inside a container: publishing a port with `-p` requires binding
0.0.0.0 in the namespace, and whether that port reaches the network is
decided by the host-side publish spec, which the process cannot observe.

So the guard refused every container started from the documented Quick
start — which publishes to loopback and was therefore safe — and with the
documented `--restart unless-stopped` that became a restart loop. Verified
against the published image: 1.28.0 exits(1) on the README command, and
starts when only an auth token is added.

Containers now warn, naming the publish spec as the thing to check. The
host rule is unchanged: `validate_http_exposure` still refuses an
unauthenticated non-loopback bind outside a container, and a test asserts
that identical inputs refuse on a host and warn in a container.

The Host allowlist deliberately does not back this up. It defends against
DNS rebinding, where a browser sets the header; a client that can route to
the port sets `Host` freely, confirmed by reaching a container's IP
directly with a forged `Host: localhost`.

Refs #407

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 12:48:18 -03:00
AkitaOnRails 1a5806e75c security: owner-guarded hook sessions, private file modes, fail-closed binds
Audit-driven hardening bundle:

- Hook session UUIDs reject cross-owner reuse atomically inside the writer
  transaction before ingest-key claim, observation, summary, handoff, or
  end-state mutation; guarded end operations revalidate the persisted
  tuple+owner; explicit root `finalize-session --all-owners` recovery is
  SessionEnd-only and Admin-gated; keyed replay serialization holds the
  ingest gate through downstream completion.
- Unauthenticated non-loopback HTTP binds now fail closed before serving;
  `--allow-insecure-no-auth` is the explicit dangerous override. Loopback
  default unchanged.
- New data dirs (0700) and config/SQLite/segment/backup files (0600) are
  created owner-only regardless of umask; existing installs untouched.
- `[auth].secure_cookie` marks the /web browser cookie Secure for HTTPS
  reverse-proxy deployments; SameSite is now Strict; proxy headers are
  never trusted to infer HTTPS.
- /api/v1 internal errors return a stable generic 500 body while the
  detailed cause is logged server-side; AuthSettings Debug redacts bearer,
  pepper, and proxy secrets.
- Multi-user per-actor autoscope contract aligned with globally unique
  durable SessionIds: distinct run IDs per user; cross-owner same-ID reuse
  is dropped before pointer publication.

Verified: 2378 workspace tests, clippy -D warnings, gitleaks, cargo
audit/deny, plus live-server auth/permission checks.
2026-08-15 18:26:14 -03:00
AkitaOnRails 6161986a77 ci: verify full-history secret scans 2026-07-30 21:41:09 -03:00
AkitaOnRails 1dfb3cc63d fix: scope and harden entity retrieval 2026-07-30 18:06:26 -03:00
AkitaOnRails 978ade15e1 fix: bound and harden optional reranking 2026-07-30 17:14:13 -03:00
AkitaOnRails 9d7ec11371 fix: harden memory and release trust boundaries 2026-07-30 00:42:38 -03:00
Samir Hanna VerzaandClaude Opus 4.8 f28029c690 feat(hooks): opt-in assistant/Stop capture for Claude Code (#196)
Builds on the default-off strip (1.17.2) to add an explicit, sanitized,
double opt-in that persists a Claude Code Stop event's final assistant
turn as the Stop body. Off by default.

Double opt-in (both required): the server sets `capture_assistant`
(config / `AI_MEMORY_CAPTURE_ASSISTANT`), and the client is installed
with `install-hooks --agent claude-code --capture-assistant`. Any gate
failure degrades to an empty Stop with the same 202 "queued".

- Protocol: `AssistantCaptureProtocol { version, excerpt }` carried in the
  body under `_ai_memory_assistant`, `deny_unknown_fields` + version gate so
  a malformed/future marker is rejected. `capture_assistant=1` query flag.
- Client (`transform_for_client`): reads the assistant message, scrubs with
  the built-in `Sanitizer` BEFORE truncating to 2 KB (UTF-8-safe), splices the
  protocol, and appends the flag to the event URL — all before spool/wire.
- Server (`apply_assistant_backstop`): consumes the marker unconditionally,
  and only when server-enabled + client-requested + supported Stop + valid
  non-empty protocol sets `body_excerpt`. Re-scrubs via `Sanitized::new` AND
  re-enforces the 2 KB cap at the persistence boundary — never trusting the
  client's length. Applied identically in `handle_hook`/`handle_hook_batch`.
- Install: `--capture-assistant` baked onto the native `stop` command only, in
  all three render paths (Posix/Windows string forms + the primary Windows
  exec form). `capture_assistant_allowed` gates it to Claude Code on a native
  platform; any other agent/platform bails (no silent no-op). Re-running
  without the flag removes it.
- Config/state: `Config.capture_assistant`, `HookState.capture_assistant_enabled`
  wired through `serve`, `HookQuery.capture_assistant` →
  `HookEnvelope.capture_assistant_requested`. `truncate_excerpt` refactored to a
  shared `truncate_utf8_bytes`, one UTF-8-safe truncation, two named caps.
- Script fallback cannot sanitize: `stop.sh` (POSIX `case`, no bash-isms) and
  the shared `ai-memory-hook.ps1` (scoped to claude-code + stop) drop a Stop
  still carrying the raw field rather than POST it verbatim.
- Tests: protocol/transform/backstop table + gates + forged/versioned markers,
  scrub-before-truncate, multibyte cap, server-side oversized-excerpt cap,
  server round-trip (both opt-ins on → body persisted; server off → empty)
  with whole-store byte scan, spool splice via subprocess, spool→drain→batch
  flag round-trip, exec/string render, and the install agent/platform gate.
- Docs: install guide (server→client order), README matrix, marker-file,
  ARCHITECTURE invariant #6, SECURITY.md (sanitize seams, LLM/cloud flow,
  global scope), config template, CHANGELOG.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 10:06:37 -03:00
AkitaOnRailsandClaude Opus 4.7 d8e0542e6d release prep: MIT license, wiki migration framework, release scaffolding
- LICENSE (MIT) + workspace license = MIT (down from MIT OR Apache-2.0)
- Wiki-structure migration framework: V06 wiki_migrations table,
  WikiMigration trait, registry, and run_pending runner (registry
  starts empty; v1 ships per-project layout natively)
- CHANGELOG.md, SECURITY.md, CONTRIBUTING.md
- bin/release (fmt/clippy/test/deny/audit -> bump -> tag; never pushes)
- .github release workflow + issue/PR templates
- README: bootstrap example defers to default workspace/project
- evals: inherit version/edition/rust-version from workspace

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 23:57:19 -03:00