Skip to content

Release/0.11.0#424

Open
itlackey wants to merge 269 commits into
mainfrom
release/0.11.0
Open

Release/0.11.0#424
itlackey wants to merge 269 commits into
mainfrom
release/0.11.0

Conversation

@itlackey
Copy link
Copy Markdown
Owner

This pull request introduces several important updates to documentation, CI/CD workflows, and the filesystem contract, primarily to improve clarity, enforce version consistency, and reflect recent architectural changes (such as the deprecation of OpenViking and the migration of stack secrets/configs). The changes also streamline the Docker build process and update the development and security documentation to match the current system design.

Key changes include:

CI/CD Workflow Improvements

  • Added CI checks to enforce version synchronization for AKM_CLI_VERSION and BUN_VERSION between the base, guardian, and channel Dockerfiles, and to ensure OPENCODE_VERSION is only declared in the base Dockerfile. This prevents silent version mismatches and ensures single sources of truth.
  • Refactored the release workflow to build and publish a shared base Docker image (openpalm-base) and updated dependent images to pull from GHCR, improving build reproducibility and reliability. [1] [2] [3]

Filesystem Contract and Secrets Migration

  • Updated documentation and .openpalm/README.md to move stack secrets (stack.env, guardian.env) from .dev/vault/stack/ to .dev/config/stack/, aligning with the new filesystem contract and clarifying the separation between user-managed secrets and system-managed config. [1] [2] [3] [4]

Deprecation and Roadmap Updates

  • Marked the OpenViking-based knowledge system and related integration plans as superseded/cancelled, referencing the new AKM-based approach. This clarifies future development focus and prevents implementation of deprecated features. [1] [2]

Documentation and Security Model Updates

  • Updated .github/CONTRIBUTING.md to clarify dev server scripts, Docker build patterns, and to remove outdated references to the admin Docker image (which is now host-only). [1] [2] [3] [4] [5]
  • Updated .github/SECURITY.md to reflect the new admin security model (host-only, direct Docker access, no socket proxy).

Cleanup and Package Publishing

  • Removed the admin-tools package from the release group and deleted its publish workflow, reflecting its deprecation or removal from active development. [1] [2]

Other minor changes:

  • Updated .dockerignore to stop ignoring core/admin/node_modules and related build artifacts, matching the new admin build process.

These changes together ensure the project documentation, build system, and security posture are up-to-date, reduce the risk of version drift, and clarify the direction for future development.

itlackey and others added 30 commits May 14, 2026 20:03
Verified across OpenCode 1.2.24, 1.3.3 (current pin), and 1.14.50 (latest):
- Hardcoded baseURL http://127.0.0.1:1234/v1 still present in models.dev catalog
- OpenCode supports `provider` (singular) config key to override but no env var
- Writing to opencode.json from entrypoint would violate "never overwrite user files"

Comment now records:
- Versions verified + date
- Pointer to upstream provider.ts mergeProvider for the override path
- Why env-var override is the missing piece
- Link to related upstream issue anomalyco/opencode#1555
- TODO to file a fresh issue requesting env-var override

When OpenCode adds env-var override, this whole function + the socat apt-get
dependency in the Dockerfile can be deleted.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…tdin pattern (closes #406) (#421)

* feat(vault): complete Phase 2 of #388 + migrate to akm secret-store stdin pattern (closes #406)

WIP — agent ran out of time before updating 5 tests to match the new architecture.
Tests failing on this branch:
- cli main > backs up the current OP_HOME (likely hang regression similar to PR #404)
- secrets.env generation > user.env as placeholder (Phase 2 deletes user.env)
- buildComposeOptions/buildComposeCliArgs (env file list changed)
- Fresh install > ensureSecrets user.env placeholder

A follow-up fix is needed to update those tests to match the Phase 2 reality.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: align 5 stale assertions with Phase 2 (#388 / closes #406) vault layout

Phase 2 retired vault/user/user.env from the compose env_file set and
no longer seeds the file on fresh install (akm vault:user is the
canonical source). Five tests still encoded the pre-Phase-2 contract
and were failing on the WIP branch:

* install flow tier 1 (3 tests):
  - "seed + performSetup..." — drop user.env from regular-files check;
    assert it is now ABSENT after a fresh setup.
  - "compose config validates..." — drop --env-file user.env from the
    compose invocation (it would error with "couldn't find env file").
  - "performSetup with no addons..." — same compose-arg fix.

* cli main "backs up the current OP_HOME before install --force":
  bootstrapInstall now uses vault/stack/stack.env as the
  "already installed" marker (commit f9e0451). Seed both stack.env
  AND a legacy user.env so the backup path triggers and we can still
  prove user.env content is preserved in the backup snapshot.

* secrets.env generation "...empty placeholder...":
  Fresh installs no longer create user.env. Assert it is absent and
  rename the test to reflect the new contract.

* Fresh Install "ensureSecrets creates user.env as placeholder...":
  Same change in the lib edge-case suite — assert user.env is absent
  while stack.env still gets the seeded keys.

No production behavior change beyond what landed in f9e0451; this
commit only updates test assertions to match. All 877 tests pass and
admin svelte-check reports 0 errors / 0 warnings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(vault): align 10 admin vitest specs with Phase 2 akm-only contract

- buildEnvFiles tests: drop user.env from expected outputs (Phase 2
  removes user.env from compose env_file list to prevent shadowing
  akm-sourced values).
- ensureSecrets / secrets directory tests: assert vault/user/ directory
  exists for operational non-env files but user.env is no longer seeded.
- user-vault route tests: rewrite to mock @openpalm/lib akm helpers
  (ensureAkmUserVault / readAkmUserVaultFile / writeAkmVaultKey /
  deleteAkmVaultKey). Real akm calls go through Bun.spawn which is
  unavailable inside vitest's Node worker pool; end-to-end coverage of
  the real akm binary lives in akm-vault.test.ts under bun test.
- Add explicit GET/POST/DELETE 503 coverage when akm is unavailable so
  the akm_unavailable path is exercised in CI.

Closes test gap from f9e0451 (Phase 2 of #388 / closes #406).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(lib): add @types/bun for Bun.spawn typing

The new akm-vault.ts uses Bun.spawn (in-memory stdin pipe). Lib's tsconfig
only declared "node" types, so admin's svelte-check failed with
"Cannot find name 'Bun'" when type-checking lib via the workspace.

Add @types/bun to lib's devDeps and declare it in tsconfig types. Tests
still pass (Bun runtime provides this globally regardless), and downstream
consumers (admin, cli) inherit correct types via workspace symlinks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Final 3-reviewer audit on release/0.11.0 surfaced 3 medium and 4 low
findings against the akm vault migration (PR #421) and adjacent surfaces.

Medium:

- secret-backend: stop reading the deleted legacy vault/user/user.env.
  Phase 2 of #388 deletes that file post-upgrade, so plaintext list/exists/
  currentValueForTarget were returning empty for user-scope secrets after
  the migration. Add akmUserVaultPathSync + readUserVaultSync helpers to
  akm-vault.ts that prefer the akm vault:user store and fall back to the
  legacy file for fresh installs that have not yet seeded akm. Add a
  regression test that simulates the post-migration state and asserts
  user-scope list/exists still resolve.
- entrypoint.sh maybe_proxy_lmstudio: validate target_host and target_port
  before handing them to socat. Restrict host to a hostname/IP character
  class and port to digits to block socat option-injection via crafted
  LMSTUDIO_BASE_URL values.
- akm-vault.test.ts argv-leak guard: make the security invariant test
  unconditional. Stub Bun.spawn (which Bun's child_process.execFile shim
  also delegates through) to synthesize akm responses end-to-end without
  needing the real binary on PATH. Capture stdin writes + argv calls and
  assert the secret value never appears on argv. Keep the live-akm test
  as a sanity check gated on AKM_AVAILABLE.

Low:

- system.md: drop the stale "falls back to /etc/vault/user.env" claim for
  load_vault. Post-#421 the only resolver is `akm vault path vault:user`.
- packages/lib barrel: re-export type SecretBackend so consumers can
  type-annotate detectSecretBackend's return. Also re-export the new
  akmUserVaultPathSync + readUserVaultSync helpers.
- voice addon: remove dead CHANNEL_VOICE_SECRET from .env.schema (voice
  serves static assets and never round-trips through the guardian).
  Document the new design inline in the schema and the channel README.
  Promote STT_BASE_URL to @required so the registry-components contract
  test (which requires at least one @required var per full addon) keeps
  passing.
- ci.yml: enforce AKM_CLI_VERSION lockstep between core/base/Dockerfile
  and core/guardian/Dockerfile so a future bump to the base image cannot
  silently leave guardian on an older akm-cli version. Update the comment
  in core/base/Dockerfile to point at the new CI gate.

Verified: bun run test (889 pass, 0 fail), bun run admin:test:unit
(505 pass, 0 fail), bun run admin:check (0 errors), validate-registry.sh
(8 addons, 0 errors).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- index: remove dead barrel exports (buildAkmEnv, MirrorResult,
  UpgradeResult, AccessScope, ActionType, AutomationAction,
  akmUserVaultPathSync) — none consumed outside lib; verified via grep
  across admin/cli/scheduler/core. Symbols stay defined in their source
  modules where lib internals (and the akm-vault.test.ts file) still
  reference them via relative imports.
- secret-backend: drop the readUserEnv 1-line alias; both call sites now
  invoke readUserVaultSync directly.
- akm-vault: extract raceWithTimeout helper used by both execAkm and
  akmVaultSetViaStdin (was ~20 lines of duplicated setTimeout/unref/race
  logic). Make ensureAkmUserVault accept a pre-built akm env so
  writeAkmVaultKey / deleteAkmVaultKey / mirrorUserVaultToAkm /
  migrateAndCleanupLegacyUserEnv stop building it twice per call.
- lifecycle: drop the redundant compose preflight at the top of
  performUpgrade — applyUpgrade -> reconcileCore already runs it. One
  fewer docker subprocess per upgrade and one fewer place that can
  drift out of sync with the canonical preflight call.
- secret-backend: keep the PassContext struct (with a clarifying comment
  on why) — inlining would duplicate the default-resolution logic across
  five helpers.

bun run test: 882 pass / 7 skip / 0 fail.
bun run admin:check: 0 errors / 0 warnings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Scripts:
- release-e2e-test.sh: read OP_ADMIN_TOKEN from vault/stack/stack.env and
  OP_CAP_LLM_PROVIDER/MODEL instead of nonexistent ADMIN_TOKEN/SYSTEM_LLM_*
  in user.env; renumber step comments to be contiguous (11–14).
- dev-setup.sh: drop retired openviking data directory from mkdir list.
- dev-e2e-test.sh: renumber Step 13/14 → 12/13 to close the numbering gap.
- upgrade-test.sh: seed and verify OP_ADMIN_TOKEN in vault/stack/stack.env
  (where it actually lives) instead of vault/user/user.env.
- run-all-tiers.sh: rewrite to delegate to scripts/test-tier.sh so tier
  definitions live in a single place.
- load-test-env.sh: add a must-be-sourced guard so direct execution fails
  loudly instead of silently doing nothing.

Docs:
- .github/CONTRIBUTING.md: drop references to nonexistent
  bun run channel:chat:dev (the chat addon is served by channel-api with
  CHANNEL_ID=chat).
- docs/technical/environment-and-mounts.md: remove the entire Memory
  service section (memory was retired in #405) and replace with a note
  pointing at the akm stash mounts; drop residual MEMORY_*/OP_MEMORY_*
  refs from the assistant, scheduler, admin, and stack.env tables.

Versions:
- Bump platform manifests (root, lib, admin, guardian, cli, channels-sdk)
  and setup.sh/setup.ps1 SCRIPT_VERSION to 0.11.0; bump CLI's
  @openpalm/lib semver range to >=0.11.0 <1.0.0; regenerate bun.lock.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Channels SDK + adapters + guardian:
- Drop the constantTimeEqual re-export shim in channels-sdk/src/utils.ts;
  re-export from crypto.ts directly via the barrel (no external consumer
  imports utils.ts directly). [HI9]
- Delete the splitMessage test blocks duplicated across channel-discord
  and channel-slack — splitMessage is fully covered in
  channels-sdk/src/utils.test.ts. Removed channel-slack's
  splitMessage re-export that only existed for those tests. [HI10]
- Hoist guardian's clientOpts() factory to a module-level CLIENT_OPTS
  const (env vars are stable). [MEDIUM]
- Simplify findExistingSessionId: drop the redundant inner condition
  that mirrored the early-return guard. [MEDIUM]

Containers + scheduler:
- Voice addon: align CHANNEL_PACKAGE with start.sh by introducing a
  CHANNEL_ENTRYPOINT override in start.sh. Voice runs its own Bun.serve
  (not BaseChannel), so it now installs CHANNEL_PACKAGE as usual and
  execs the package's index instead of the SDK channel-entrypoint. [M5]
- Admin addon: pass through the full provider key set (LMSTUDIO_*,
  TOGETHER, DEEPSEEK, XAI, HF_TOKEN, MCP_API_KEY, EMBEDDING_API_KEY,
  OP_CAP_LLM_PROVIDER) so admin's OpenCode sees the same providers as
  the assistant. [M6]
- Drop azure-cli, huggingface-hub[cli], mgc, dbus/gnome-keyring/
  libsecret-1-0 from the assistant Dockerfile and the matching
  MGC_CONFIG_DIR env from compose — no caller. Keep gcloud + gws
  (used by the gws-setup skill) and apprise (used by notify). [H1, H2]
- Refactor entrypoint.sh start_opencode from 4 near-duplicated branches
  to a single command-array build (with optional gosu prefix). [M3]
- Refactor maybe_enable_ssh to check id -u once at the top. [M4]

CI:
- Add CI sync checks asserting OPENCODE_VERSION is declared only in
  core/base/Dockerfile (admin/assistant inherit) and that BUN_VERSION
  major.minor in core/base matches the oven/bun image tag in
  core/guardian + core/channel. [M2]
- HI3: drop spurious `await` from 11 sync `ensureValidState()` call sites.
- HI4: remove dead `bootstrapInstall` re-export from `main.ts`.
- HI5: move volume-mount-target pre-creation into
  `@openpalm/lib`'s `ensureComposeVolumeTargets()` (called from
  `applyInstall`), eliminating 65 lines of duplicated compose parsing
  in `install.ts`. Tighten the file/dir heuristic so leading-dot files
  (e.g. `.env`) are correctly treated as files instead of directories.
- Move env helpers (`upsertEnvValue`, `resolveRequestedImageTag`,
  `reconcileStackEnvImageTag`, `RELEASE_TAG_REGEX`) from CLI into
  `@openpalm/lib`'s `control-plane/env.ts` and import from lib in CLI.
- Drop unused `unwrapQuotedEnvValue` (had no callers).
- Split misnamed `cli/lib/docker.ts` (249 lines) into focused modules:
  * `docker.ts` keeps only `runDockerCompose` / `runDockerComposeCapture`
  * new `io.ts` owns directory-tree, asset fetch, copy-tree, dir-exists,
    seedOpenPalmDir
  * new `browser.ts` owns `openBrowser`
- Replace BSD/GNU-incompatible `find` subprocess in `copyTree` with
  `node:fs` recursive `readdirSync` — portable, no subprocess.
- Update `install-flow.test.ts` to call the lib helper directly instead
  of replicating the volume-mount logic via a `cast as any` access of
  the (now-removed) private CLI function.

Net CLI reduction: ~131 lines. All existing tests (889) pass; admin
svelte-check is clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Audit #2 cleanup for the admin package:

- HI6: collapse three diverging asString/asRecord definitions to a
  single source in `lib/server/coercion.ts`. Provider routes now use
  `asStringOrEmpty` for the trimmed/empty-string semantics they need;
  `provider-models.ts` imports `asRecord` directly instead of redefining
  a null-returning copy.
- HI7: delete `lib/server/scheduler.ts`. The remaining caller now imports
  `loadAutomations` from `@openpalm/lib` directly.
- Inline `lib/server/capabilities.ts` (10-line wrapper around 4 lib calls)
  at its two call sites and delete the file.
- Inline `lib/opencode/client.server.ts`. Helpers exposes a lazy
  `getOpenCodeClient()` singleton bound to the configured base URL;
  routes call it directly instead of importing 7 named re-exports.
  Vitest mocks now stub `getOpenCodeClient` from helpers.
- OverviewTab.svelte: drop the dead `services` and `adminStatus` props
  (and the now-orphan `services` derived block in the parent page).
- packages/admin-tools/AGENTS.md: trim runtime persona content; the
  assistant guidance lives in `core/assistant/opencode/` and this file
  is a contributor pointer only (mirrors the assistant-tools AGENTS.md
  pattern from #410).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Docs cleanup (3 files outside WS1's scope):
- docs/channels/community-channels.md: removed channel-chat reference (channel-api serves chat addon via CHANNEL_ID=chat)
- docs/technical/bunjs-rules.md: replaced channel-chat example with channel-discord/channel-slack
- docs/technical/opencode-configuration.md: replaced MEMORY_API_URL/MEMORY_AUTH_TOKEN/MEMORY_USER_ID rows with AKM_STASH_DIR/AKM_CACHE_DIR (memory served via akm stash)

Route helper:
- Added withAdminBody() to packages/admin/src/lib/server/helpers.ts to wrap the 4-line auth+JSON-body preamble that was copy-pasted across 30+ admin POST routes
- Migrated 5 provider routes as proof-of-pattern: providers/save, providers/toggle, providers/model, providers/oauth/start, providers/oauth/finish

Each migrated route loses 5 lines of boilerplate. Future routes can opt in.

Misc:
- Add .claude/ to .gitignore (per-session worktree directories should never be tracked)

Verified: bun run admin:check 0 errors, bun run admin:test:unit 499/499 pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The "one-shot" framing in the docstring implied no leftover state on the
assistant, but the function was leaking sessions on every call. Schedulers
running daily prompts via askAssistant would accumulate sessions
indefinitely.

Behavior change:
- Default: try/finally now calls deleteSession after sendMessage
- Cleanup is best-effort (.catch swallows failures so the answer takes priority)
- Pass `keepSession: true` to opt out (for follow-up message flows)

Tests:
- Updated existing happy-path test to expect 3 calls (create + send + delete)
- Updated message-timeout test mock to handle DELETE (otherwise the cleanup
  fetch hangs against the never-resolving mock)
- Added new test asserting keepSession: true skips the cleanup

This is a breaking change for external SDK consumers relying on session
persistence, but no internal OpenPalm code uses askAssistant for that
pattern (verified via grep).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CHANGELOG.md: add [0.11.0] section (Added/Changed/Fixed/Removed/Security)
- capability-injection.md: remove stale memory service row + Memory capability slot
- api-spec.md: remove all /admin/memory/* routes and memory from service lists
- installation.md, system-requirements.md: replace MEMORY_* vars with OP_CAP_*
- managing-openpalm.md: replace data/memory with data/stash, add port 3881
- password-management.md: replace MEMORY_* and SYSTEM_LLM_* with OP_CAP_* vars
- how-it-works.md: replace memory service bullet with akm stash
- backup-restore.md, troubleshooting.md: remove data/memory and port 3898
- setup-walkthrough.md: remove memory from core services + step 5 options
- setup-guide.md: replace .env.schema troubleshooting hint with compose.yml
- manual-compose-runbook.md: remove memory from core services description
- testing-workflow.md: remove OpenMemory Tier 5/6 test items
- README.md, docs/README.md: remove memory service references

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…th akm

- entrypoint.sh: add maybe_configure_akm() — calls `akm setup --config`
  with OP_CAP_* vars on container start so akm always uses the configured
  provider. SLM preferred over LLM for akm (lightweight operations).
  Includes embedding config when OP_CAP_EMBEDDINGS_* are set.

- entrypoint.sh: fix maybe_unset_unused_provider_keys() — now considers
  both LLM and SLM providers before unsetting any key. Prevents the
  scheduler's akm improve/distill calls (which use SLM) from losing their
  API key when LLM and SLM use different providers.

- lib: add buildAkmSetupJson(spec, stackEnv) to spec-to-env.ts — builds
  the akm setup config JSON from a StackSpec. SLM preferred; falls back
  to LLM. Exported from lib index. 19 tests cover all paths.

- assignments route: call buildAkmSetupJson after writeCapabilityVars and
  write to data/stash/.config/config.json — so akm is configured
  immediately on capability save without waiting for container restart.

- CapabilitiesTab: rename "Capabilities" sub-tab to "AI Models"; add
  contextual descriptions explaining akm uses SLM for stash improvement/
  memory consolidation and embeddings for semantic search. Voice tab gets
  a note that TTS/STT are for voice channel and web audio, not akm.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…all save paths

Dead code removed (all introduced before akm alignment):
- fetchCapabilities() and CapabilitiesResponseDto/CapabilitiesSummary types — masked
  secrets were fetched on capabilities tab switch but never rendered in the UI
- capabilitiesData, capabilitiesLoading, loadCapabilities() from +page.svelte
- loading and onRefresh props from CapabilitiesTab (parent had no meaningful value
  to pass; component manages its own load state)
- api.vitest.ts deleted — all 3 tests were for the removed fetchCapabilities

akm config now written on all capability save paths:
- Old /admin/capabilities POST (setup wizard path) now also calls buildAkmSetupJson
  and writes data/stash/.config/config.json, matching the assignments route

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…emory capability

Directory layout (v0.11.0):
  data/stash/          — akm STASH: asset content only (skills, vaults, knowledge)
  data/akm/            — NEW: akm operational data (config / data / state)
    config/            → AKM_CONFIG_DIR (was: data/stash/.config)
    data/              → AKM_DATA_DIR   (was: data/stash/.data)
    state/             → AKM_STATE_DIR  (was: data/stash/.state)
  data/akm-cache/      — cache (unchanged)
  data/guardian-stash/ — guardian-only stash (unchanged)
  data/guardian-akm/   — NEW: guardian akm operational data

Changes:
- core.compose.yml: init mkdir creates data/akm/* and data/guardian-akm/*
  instead of stash hidden dirs; assistant + guardian get /akm-data and
  /akm-guardian-data bind mounts; AKM_CONFIG/DATA/STATE_DIR updated
- admin compose: same AKM env and /akm-data mount
- akm-vault.ts: buildAkmEnv() uses data/akm/{config,data,state} instead
  of nested stash paths; updated layout comment
- home.ts: ensureHomeDirs() creates new akm and guardian-akm trees;
  removes stale stash/.{config,data,state} entries
- capabilities routes: akm config write target updated to data/akm/config

Memory capability cleanup:
- setup-config.schema.json: remove deleted memory service fields
  (memoryUserId, memory subsystem descriptions)
- spec-validator.ts: remove capabilities.memory required validation
- setup.test.ts: remove stale memory capability from test fixture

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ory taxonomy

New ~/.openpalm/ layout:
  config/       — user-editable config (unchanged)
  stash/        — akm knowledge: skills, vaults, knowledge, agents
  workspace/    — shared work area
  services/     — container bind mounts
    assistant/  — assistant HOME (/home/opencode)
    admin/      — admin home
    guardian/   — guardian + guardian/stash + guardian/akm
  state/        — system-managed state (replaces vault/ + data/ + logs/)
    stack.env   — was vault/stack/stack.env
    guardian.env — was vault/stack/guardian.env
    auth.json   — was vault/stack/auth.json
    akm/config/ — akm operational config (was data/akm/)
    scheduler/  — trigger sentinels (was data/scheduler/)
    logs/       — was logs/
    backups/    — was backups/
    registry/   — was registry/
    cache/akm,guardian,rollback — all regenerable data
  stack/        — compose runtime files (unchanged)

Also: paths.ts — central path-resolution module
  All path strings centralized in packages/lib/src/control-plane/paths.ts
  (stackEnvPath, guardianEnvPath, authJsonPath, akmConfigPath, logsDir,
  registryDir, rollbackDir, akmUserVaultPath, assistantServiceDir, etc.)
  Consumers import typed functions instead of concatenating strings inline.

Files changed: 74 files, 947 insertions, 1394 deletions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Final ~/.openpalm/ structure:
  config/      — user config + system config (stack.env, guardian.env, auth.json, akm/)
  cache/       — regenerable/semi-persistent (akm, guardian, rollback)
  state/       — persistent service data (assistant, admin, guardian, logs, backups, registry)
  stash/       — akm knowledge (unchanged)
  workspace/   — shared work area (unchanged)
  stack/       — compose runtime (unchanged)

Key moves from previous iteration:
  services/ eliminated — service bind mounts now live under state/
    state/assistant/   (was services/assistant/)
    state/admin/       (was services/admin/)
    state/guardian/    (was services/guardian/)
  state/cache/ eliminated — cache promoted to top-level cache/
    cache/akm/         (was state/cache/akm/)
    cache/guardian/    (was state/cache/guardian/)
    cache/rollback/    (was state/cache/rollback/)
  stack.env, guardian.env, auth.json moved to config/
    config/stack.env   (was state/stack.env)
    config/guardian.env (was state/guardian.env)
    config/auth.json   (was state/auth.json)
  akm config moved to config/
    config/akm/        (AKM_CONFIG_DIR — was state/akm/config/)

ControlPlaneState: servicesDir removed, cacheDir added
paths.ts: all path functions updated to match new layout
home.ts: ensureHomeDirs() creates new tree; resolveCacheDir() added
akm-vault.ts: buildAkmEnv() uses configDir/akm and cacheDir/akm
Compose: AKM_CONFIG_DIR=/etc/openpalm/akm (assistant, via existing config mount)
         AKM_CONFIG_DIR=/openpalm/config/akm (admin, via full OP_HOME mount)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
….env, stack.yml now live at config/stack/

Completes the v0.11.0 directory restructuring. All top-level dirs under
OP_HOME are now: config/ cache/ state/ stash/ workspace/. Stack.env,
guardian.env, and stack.yml move from config/ into config/stack/
alongside the compose overlays and addons.

- Central paths.ts module resolves all paths through ControlPlaneState
- secrets.ts, lifecycle.ts, config-persistence.ts, spec-to-env.ts all
  updated to use state.stackDir for stack.env and guardian.env
- registry.ts, channels.ts, core-assets.ts updated to config/stack/addons
- rollback.ts SNAPSHOT_FILES updated to new relative paths
- CLI io.ts ensureDirectoryTree rewritten to match v0.11.0 layout
- All 873 bun tests, 496 admin unit tests, 67 CLI tests pass
- svelte-check: 0 errors

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…state/assistant/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Update 38 documentation files to reflect the migration of stack directory
into config/stack/. Key changes:

- Stack configuration (core.compose.yml, addons/) moved from OP_HOME/stack/
  to OP_HOME/config/stack/
- System-managed env files moved from vault/stack/ to config/stack/:
  - stack.env (system-managed env vars and secrets)
  - guardian.env (channel HMAC secrets with hot-reload)
- Vault now contains only user-managed secrets (vault/user/)
- Updated all path references across operational docs, technical docs,
  setup guides, channel documentation, and README files
- Created new .openpalm/config/stack/README.md with updated structure
- Marked old .openpalm/stack/README.md as deprecated with migration note

Files updated:
- Operational docs: manual-compose-runbook, troubleshooting, setup guides
- Technical docs: core-principles, api-spec, registry, environment-and-mounts
- Channel docs: all channel-specific READMEs
- Supporting docs: AGENTS, CONTRIBUTING, installation, backup-restore

All 38 files verified with no remaining old path references.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Critical fixes for directory structure migration:

CLI Installation Flow:
- Fix install.ts: check "already installed" marker at config/stack/stack.env
- Fix install.ts: create guardian.env at config/stack/ instead of state/
- Fix install.ts: pass configDir to ensureStackEnv (not stateDir)
- Fix env.ts: write stack.env to config/stack/ instead of state/
- Fix io.ts: seed core.compose.yml from correct repo location

E2E Test Harness:
- Fix e2e/global-setup.ts: point to .dev/config/stack/stack.env
- Fix e2e/global-teardown.ts: point to .dev/config/stack/stack.env
- Fix e2e/channel-guardian-pipeline.pw.ts: point to .dev/config/stack/guardian.env

Test Updates:
- Fix main.test.ts: seed and verify stack.env at new config/stack/ location
- Update core-assets.vitest.ts: clarify old paths are not resurrected
- Update auth server test name: reference config/stack/stack.env
- Update lifecycle-validate comment: reference config/stack/stack.env
- Update staging comment: reference config/stack paths
- Update route handler comment: reference config/stack paths

All 67 CLI tests passing, all 496 admin tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Deletes the `packages/scheduler/` co-process entirely and delegates all
scheduling, execution, and history to the AKM task system. The assistant
container now starts crond at boot via entrypoint.sh and calls `akm tasks sync`
to register task files with OS cron; a 60 s background loop re-syncs to pick up
files written by the admin container.

Breaking changes:
- `config/automations/*.yml` files are no longer read; automation task files
  are now AKM markdown in `stash/tasks/*.md`
- Per-task `timezone:` is dropped; set container-wide TZ in stack.env
- Sentinel-file triggers removed; use `POST /admin/automations/:name/run`
- Execution latency increases from ~10 ms (inline) to ~80 ms (CLI spawn)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implements the first two phases of the host-admin migration proposal
(docs/technical/proposals/host-admin-migration.md).

Phase 1a — Server proof-of-concept:
- `openpalm admin serve` command: Bun.serve gateway on 127.0.0.1:3880
  extracts SvelteKit build tarball to ~/.openpalm/cache/admin/{version}/
  and serves it from disk (adapter-node output, no path patch needed)
- Host OpenCode subprocess with SIGTERM/SIGINT wiring and crash recovery
- Proxy routes /proxy/assistant and /proxy/admin to respective backends
- OPENPALM_ADMIN_MODE=host|container feature flag (default: container)
- Cookie auth foundation: /admin/auth/session issues httpOnly cookie
- Host header allowlist (closes DNS rebinding) + Origin check on mutations
- Admin skills allowlist with 4-invariant argument validation
- admin-token.ts: ensureAdminToken / rotateAdminToken at mode 0600
- Windows: symlinkSync → copyFileSync guard in opencode-subprocess.ts

Phase 1b — Chat UI (primary user surface):
- /chat route: default landing after setup, Svelte 5 runes, full state
- Assistant/Admin toggle with visual thread segmentation on switch
- ChatMessage + ChatInput components; 150s timeout on all chat requests
- /proxy/assistant and /proxy/admin catch-all SvelteKit routes
- Existing admin dashboard moved to /admin; root / redirects to /chat
- Voice TTS wired to chat responses via voice-state.svelte.ts
- OP_ADMIN_OPENCODE_INTERNAL_URL added to compose.dev.yml

Test results:
- cli:test: 92 pass / 0 fail
- admin:check: 0 errors / 0 warnings (676 files)
- admin:test:unit: 459 pass / 0 fail
- guardian:test: 31 pass / 0 fail
- sdk:test: 39 pass / 0 fail

Phase 2 gate: requires Phase 1a/1b to soak for ≥ 1 release.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Workstream A — Push appendAudit into lib:
- Add AuditContext type to @openpalm/lib types
- applyInstall/applyUpdate/applyUninstall now call appendAudit internally
  with optional ctx parameter; direct appendAudit calls removed from routes

Workstream B — Auth migration (x-admin-token → cookie):
- Add /admin/auth/login + /admin/auth/logout routes (httpOnly cookie)
- requireAdmin/requireAuth accept cookie OR x-admin-token (both work)
- Delete packages/admin/src/lib/auth.ts (localStorage token)
- Remove token: string param from all 23 api.ts functions
- Update 21 vitest files to use session cookie instead of x-admin-token header

Workstream C — 52 routes to Bun.serve handlers:
- Add packages/admin/src/server/router.ts (path-matching router)
- Add packages/admin/src/server/entry.ts (Bun.serve entrypoint)
- Add packages/admin/src/server/shim.ts (Request → RequestEvent adapter)
- Add 55 handler files in packages/admin/src/server/routes/
- Add "start:bun" script to packages/admin/package.json

Workstream D — Default mode cutover:
- resolveAdminMode() now defaults to 'host'
- CLI install --admin-mode defaults to 'host'
- state.ts audited: no container-mode hardcoded paths

Workstream E — Docker lib consolidation:
- Move inspectContainerStatus into @openpalm/lib docker module
- Move runPreflight into lib docker module (injected into compose* fns)
- Export inspectContainerStatus from lib barrel
- Admin routes import from @openpalm/lib directly

Workstream F — Automations check command:
- Add openpalm automations check command
- Detects type:api tasks needing host-cron migration
- automations/catalog/refresh flags deprecated api-type tasks

Test results:
- admin:check: 0 errors / 0 warnings (679 files)
- admin:test:unit: 463 pass / 0 fail (+4 new cookie-path tests)
- cli:test: 92 pass / 0 fail
- guardian:test: 31 pass / 0 fail
- sdk:test: 39 pass / 0 fail

Net: 367 insertions, 473 deletions — codebase is smaller after migration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…cket-proxy

Tier 1 (parallel deletions):
- Delete core/admin/ — Dockerfile, entrypoint.sh, opencode config
- Delete .openpalm/registry/addons/admin/ — compose.yml + .env.schema
- Delete packages/admin-tools/ — 35 HTTP-wrapper tools + skills
- Remove OP_ADMIN_API_URL from core.compose.yml assistant env
- Remove admin/docker-socket-proxy from CI health check scripts

Tier 2 (code cleanup):
- Remove selfRecreateAdmin from lib/docker.ts, index.ts, admin wrapper,
  vitest, and upgrade route (--profile admin no longer exists)
- Simplify OptionalServiceName → never, OPTIONAL_SERVICES → [] in lib types
- Remove OPENPALM_ADMIN_MODE feature flag from all sources (host is always
  the mode now; no conditional branches remain)
- Clean SSRF blocklist: remove "admin" and "docker-socket-proxy" entries
- Delete docs/technical/docker-dependency-resolution.md + all references

Tier 3 (docs):
- core-principles.md: rewrite invariant #1 (admin is host process),
  add invariant #6 (admin is host-only, enforced by OS loopback)
- foundations.md: remove admin_docker_net network, replace Admin Addon
  section with "Admin (host process)" + "UI-first principle"
- Update environment-and-mounts.md, opencode-configuration.md,
  system-requirements.md, package-management.md, cli README, stack
  READMEs, AGENTS.md, CLAUDE.md — remove all container-era admin refs

Also removed:
- .github/workflows/publish-admin-tools.yml (package gone)
- .github/release-package-groups.json admin-tools entry
- .github/workflows/ci.yml admin Dockerfile audit entry
- compose.dev.yml admin service block
- packages/admin/e2e/opencode-ui.pw.ts admin-tools test block

Final state:
- 0 container images for admin (−1 from before)
- 0 Docker services for admin + docker-socket-proxy (−2)
- 0 networks: admin_docker_net gone (−1)
- 0 packages: admin-tools gone (−1)
- admin:check: 0 errors / 0 warnings
- admin:test:unit: 459 pass / 0 fail
- cli:test: 92 pass / 0 fail
- guardian:test: 31 pass / 0 fail
- sdk:test: 39 pass / 0 fail

Net: 101 insertions, 2786 deletions — codebase is substantially smaller.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Source code:
- stop.ts, uninstall.ts: remove stale comments about admin.yml/socket-proxy
- host-admin-server.ts: fix proxy comment ("container admin" → "host admin OpenCode subprocess")
- admin.ts: remove dead --container-admin flag and containerAdminBaseUrl arg
- config-persistence.ts, spec-to-env.ts: remove OP_DOCKER_SOCK (no consumer since docker-socket-proxy deleted)

Test fixtures:
- CapabilitiesTab.svelte.vitest.ts: remove stale localStorage.setItem for adminToken
- SecretsTab.svelte.vitest.ts: remove 3x stale localStorage.setItem for adminToken

Docs:
- assistant-tools/README.md: replace deleted @openpalm/admin-tools reference
- how-it-works.md: remove docker-socket-proxy references (×2)
- environment-and-mounts.md: remove OP_DOCKER_SOCK row

Tests: admin:check 0 errors, admin:test:unit 459/0, cli:test 92/0

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dead code removed:
- host-admin-server.ts: delete /proxy/admin Bun gateway handler
  (always returned 503; SvelteKit route handles /proxy/admin correctly)
- host-admin-server.ts: remove containerAdminBaseUrl from options type
- cli/src/lib/env.ts: remove OP_DOCKER_SOCK from stack.env generation
  (docker-socket-proxy deleted in Phase 3; no container reads this var)

Comment and doc fixes:
- core/assistant/opencode/system.md: "admin container" → "admin process"
- core/assistant/entrypoint.sh: fix cron sync comment (admin is host process)
- docs/technical/api-spec.md ×2: remove "admin container" from upgrade
  and network/check endpoint descriptions
- docs/troubleshooting.md: replace docker compose exec admin instructions
  with host process equivalents (lsof, openpalm admin)
- docs/operations/diagnostic-playbook.md: replace docker exec env inspect
  with host process equivalent (ps + stack.env grep)
- packages/admin/e2e/scheduler.pw.ts: fix comment + port 8100→3880,
  use ADMIN_URL env var with default

Tests: admin:check 0 errors, admin:test:unit 459/0, cli:test 92/0

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Registry automations:
- health-check.md, update-containers.md, validate-config.md: replace
  http://admin:8100 curl commands (dead — admin is no longer a container
  reachable from inside the assistant) with openpalm CLI equivalents

CLI:
- admin.ts: remove enable/disable/status subcommands for the admin addon
  (addon deleted in Phase 3; commands would always fail with "not found")
- admin.ts: remove unused listEnabledAddonIds + runAddonEnable/DisableAction imports
- main.test.ts: remove test assertions for deleted admin enable/disable/status

Docs:
- how-it-works.md: remove http://admin:8100 from architecture example
- managing-openpalm.md: replace http://admin:8100 automation example
- scheduler.vitest.ts: replace http://admin:8100 test fixture command
- .gitignore: remove packages/admin-tools/dist/index.js (package deleted)
- opencode-configuration.md: remove registry/addons/admin + core/admin/entrypoint.sh refs
- testing-workflow.md: remove admin-tools from test suite description
- release-publish-remediation-plan.md: remove admin-tools workflow references (×2)
- .github/SECURITY.md: replace docker-socket-proxy bullet with host-admin model

Tests: admin:check 0 errors, admin:test:unit 459/0, cli:test 92/0, guardian 31/0

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… docs

Svelte components:
- ContainersTab.svelte: remove docker-socket-proxy from container overlay comment
- OverviewTab.svelte: same

Docs:
- troubleshooting.md: remove port 3881 (admin OpenCode, container-era)
- managing-openpalm.md: remove http://localhost:3881 row from ports table

Tests: admin:check 0 errors, admin:test:unit 459/0, cli:test 92/0

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dev scripts:
- package.json dev:stack + dev:build: remove --enable-addon admin and
  -f .dev/stack/addons/admin/compose.yml (admin addon deleted)
- scripts/dev-e2e-test.sh: same — remove admin addon from dev_compose()
- scripts/test-tier.sh: same

Docs:
- core-principles.md: remove Admin OpenCode port 3881 from port assignments table

Tests: admin:check 0 errors, admin:test:unit 459/0, cli:test 92/0

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…books

.dockerignore: remove core/admin/ entries (directory deleted in Phase 3)

Docs:
- installation.md: remove admin addon cp command + admin row from addon table
- .openpalm/README.md: remove admin addon from example compose command + cp
- docs/operations/manual-compose-runbook.md: remove admin addon compose -f flags (×2)
- docs/troubleshooting.md: remove admin addon compose -f flags (×2)
- docs/technical/opencode-configuration.md: replace admin addon compose ref with
  host subprocess description

Tests: admin:check 0 errors, admin:test:unit 459/0, cli:test 92/0

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
itlackey and others added 30 commits May 26, 2026 10:12
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The system-check endpoint tries to bind a fresh TCP server on the admin
port to test availability. When the UI server itself is already listening
on that port (PORT env var set by Electron / cli serve), the bind always
fails and the wizard blocks with "port 3880 is in conflict" even on a
clean machine.

Fix: skip the bind test for the port the current server process is already
holding. The port is available from the stack's perspective — the UI
server will continue holding it both before and after install.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes before public npm publish:
- add files whitelist to packages/cli (prevented full src/ publish)
- wrap lifecycle endpoints in try-catch for structured error responses
- add types field to @openpalm/lib package.json
- update SECURITY.md supported versions (0.9.x → 0.11.x) and remove Caddy reference

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
requireAdmin was updated to validate op_session cookies against an
in-memory session store (validateSession) rather than comparing
the cookie value directly to OP_UI_LOGIN_PASSWORD.

All 18 failing test files still presented the password as the cookie
value, which validateSession always rejected (the password was never
seeded into the session map).

Fix:
- Add _seedSession / _clearSessions helpers to session-store.ts for
  test use
- Update resetState() in test-helpers.ts to seed the password value as
  a valid session so every test that calls resetState('token') can
  continue using that value as the op_session cookie
- Fix proxy/assistant streaming test which set the env var manually
  without seeding a session (stale comment referenced old direct-compare
  behavior)

Result: 530 passed, 0 failed (was 406 passed, 124 failed)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CLI error handling: all 10 command run() handlers now wrap in try-catch
  and call process.exit(1) on failure for reliable CI/script exit codes
- stack.yml seed stripped to version:2 only (stale capabilities block removed)
- CHANGELOG stale OP_CAP_* references corrected to akm config.json

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove dead code from capabilities → akm config migration:
- dead readStackSpec import in lifecycle.ts
- unused stackSpecFilePath export in paths.ts
- stackSpecPath helper (production-dead, inline the concat)
- unused spec: StackSpec param in deriveSystemEnvFromSpec
- stale wizard comment referencing stack.yml capabilities.tts.provider

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Implemented smoke tests for the setup wizard browser using Playwright.
- Created unit tests for AuthGate, ChatInput, ChatMessage, FriendlyError, LogsTab, ProvidersPanel, SecretsTab, SessionPicker components using Vitest.
- Added server route tests for GET /admin/health and GET /admin/providers to ensure proper authentication and response structure.
- Ensured coverage for various states including loading, error handling, and user interactions.
- Implemented tools for managing Docker Compose lifecycle: compose.up, compose.down, and compose.ps.
- Added health-check tool to monitor OpenPalm services.
- Introduced endpoints-list tool to retrieve configured OpenCode endpoints.
- Created secrets-list-keys tool to list secret names without exposing values.
- Established a plugin structure to expose these tools for use in the Electron environment.
- Included comprehensive tests for all tools to ensure functionality and security.
- Configured TypeScript settings for the project.
writeSystemEnv was writing OP_SETUP_COMPLETE=false when the key was
absent, overriding the OP_UI_LOGIN_PASSWORD fallback in isSetupComplete.
Users whose initial deploy health-check timed out never had the explicit
flag written, so upgrading to beta11 caused every request to redirect to
/setup. Fix mirrors isSetupComplete semantics: promote legacy installs
that have OP_UI_LOGIN_PASSWORD to OP_SETUP_COMPLETE=true on startup.

Voice profile section was hiding CUDA/ROCm options behind a collapsed
<details> "Advanced" element and always defaulting to CPU even when
NVIDIA hardware is available. Replace with a plain visible select and
update resolveDefaultProfile to prefer the first available GPU profile.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
isSetupComplete was short-circuiting on any explicit OP_SETUP_COMPLETE
value, so OP_SETUP_COMPLETE=false (written by old code) blocked the
OP_UI_LOGIN_PASSWORD fallback even for users who completed setup. Fix:
treat OP_UI_LOGIN_PASSWORD present as sufficient, same as absent flag.

getAddonProfiles was reading the enabled-addon copy first — which for
existing users is a stale copy from an earlier beta that only had the
cpu profile. Reverse priority: registry is always current; enabled copy
is only the fallback when the registry entry is missing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…build resolution

compareVersionTags split on '.' and mapped to Number, turning "0.11.0-beta.4"
into [0, 11, NaN] — NaN !== NaN is always true in JS, making every beta-vs-beta
comparison non-deterministic. Replace with proper semver pre-release parsing:
- numeric version segments compared numerically
- pre-release part split on '.' with each identifier compared numerically
  when both are digits (so "beta.4" < "beta.11"), or lexicographically otherwise
- stable release > pre-release per semver spec

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Added functionality to fetch releases from GitHub API, including release tags and UI build availability.
- Updated admin page to manage selected image and UI tags, integrating release data.
- Refactored AKM configuration handling to align with the new schema, removing deprecated fields and improving validation.
- Adjusted automation endpoints to support YAML file formats instead of Markdown.
- Cleaned up unused imports and improved error handling in various server routes.
- Added support for selecting hardware profiles for the bundled OpenPalm Voice addon.
- Introduced a new VoiceProfileSelector component for profile selection.
- Updated the setup wizard to include options for enabling voice and including Ollama.
- Enhanced the SystemCheckStep to detect GPU availability and update the UI accordingly.
- Modified the ReviewStep to display selected voice profiles and their statuses.
- Implemented API endpoint to fetch available voice profiles and selected profile.
- Improved model selection logic to apply imported model preferences.
- Updated `setAddonEnabled` and `setAddonProfileSelection` functions to accept state for writing run scripts upon changes.
- Introduced `resolveActiveProfiles` to deduplicate and retrieve active Docker Compose profiles from the stack environment.
- Enhanced `buildComposeOptions` to include profiles in the Docker Compose CLI arguments.
- Modified `writeRunScript` to generate a script that includes profile information for Docker Compose.
- Updated various commands and lifecycle functions to utilize the new profile handling, ensuring that profiles are set and persisted correctly.
- Added Ollama profile management to the setup process, allowing for default profile selection based on GPU detection.
- Created a new API endpoint to fetch available Ollama profiles and the selected profile.
- Updated UI components to support Ollama profile selection and display.
- Refactored setup scripts and development environment setup to accommodate new state management and directory structure.
- Removed unused imports and functions related to provider API key management in PATCH handler.
- Updated import-host route to streamline service restart logic, focusing on the assistant service.
- Enhanced test coverage for provider registration and import functionality, ensuring API keys are not stored in environment variables.
- Adjusted file paths for secrets storage from `config/stack/secrets` to `stash/vaults/secrets` for better organization.
- Modified setup scripts to reflect new secrets storage structure and ensure proper permissions.
- Improved error handling and logging in various service interactions.
- Removed addon compose file snapshotting and restoration in rollback.ts.
- Updated setup-config.schema.json to reflect new secrets path.
- Adjusted setup.test.ts comments for clarity on automation locations.
- Modified setup.ts to clarify addon enabling process.
- Updated skeleton-guardrail.test.ts to reflect changes in directory structure.
- Enhanced stack-spec tests to include addon round-trip validation.
- Implemented validation for addon names in stack-spec.ts.
- Removed registry references from ui-assets.ts and adjusted seeding logic.
- Updated core-assets.vitest.ts to ensure fallback behavior for missing files.
- Revised lifecycle.vitest.ts to include fixed custom compose file instead of addons.
- Changed addon handling in admin routes to utilize stack.env for profiles.
- Updated dev-setup.sh to modify how addons are enabled, focusing on COMPOSE_PROFILES.
- Removed the guardian service from core.compose.yml as it is no longer needed.
- Updated backup-restore documentation to reflect changes in stack.env and added descriptions for new compose files.
- Revised community channels documentation to clarify the process for adding channels and services.
- Adjusted how OpenPalm manages addon activation, moving from enabled-addons.json to stack.yml for first-party addons.
- Updated installation and setup guides to reflect the new stack configuration structure.
- Modified managing OpenPalm documentation to clarify the purpose of various stack files.
- Enhanced manual compose runbook to provide clearer instructions on managing profiles and services.
- Updated technical documentation to reflect changes in addon management and stack structure.
- Adjusted CLI tests to validate changes in stack.yml instead of stack.env.
- Updated control-plane logic to read from stack.yml for addon activation.
- Refactored dev setup script to write enabled addons directly to stack.yml.
- Moved operational data and logs from `state/` to `cache/` for better organization.
- Updated documentation to reflect new directory structure.
- Adjusted backup processes to store UI builds and other artifacts in `cache/backups/`.
- Modified tests and code references to align with the new paths.
- Ensured that all relevant directories are created during setup and installation.
… across the codebase

- Updated references in test files and server routes to use 'data' instead of 'cache' and 'state'.
- Adjusted log reading and writing functions to align with new directory structure.
- Modified helper functions and state management to reflect the new 'data' directory.
- Added new README files for assistant tools and instructions.
- Created necessary directory structure with .gitkeep files to ensure persistence.
Rename the host-side OP_HOME/stash directory to OP_HOME/knowledge across
the codebase: path resolver, source path builders, compose mount default,
secret/channel file paths, scripts, docs, tests, and ignore/CI files. The
repo skeleton dir .openpalm/stash is moved to .openpalm/knowledge.

Scope is the host path only (clean break, no migration). The container
mount target /stash, AKM_STASH_DIR, OP_AKM_STASH override name, and the
stashDir/resolveStashDir identifiers are intentionally retained — /stash
is the akm CLI's own convention and renaming it would break akm.

Tests: cli 46, lib 310, sdk 33, guardian 35, ui:check 0 errors,
ui vitest 608. Both edited compose files validate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ship two example convenience wrappers in the .openpalm/ skeleton (they
copy to OP_HOME/ root on install) that drive the stack with the same
docker compose invocation the CLI and admin UI use: up / down / restart /
upgrade / status / logs, plus a compose passthrough.

They replicate the control plane's exact invocation — project name,
overlay order (core → services → channels → custom, only those present),
--env-file stack.env, and COMPOSE_PROFILES from stack.env. OP_HOME
defaults to the script's own directory. `upgrade` only pulls images and
recreates containers; the help text is explicit that it does not refresh
assets or the UI build the way `openpalm update` does, and that the CLI
and admin UI remain the canonical orchestrators.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Relocate OpenCode's auth.json from config/auth.json to config/stack/auth.json
so a single file backs every OpenCode-based container. authJsonPath() now
resolves to ${stackDir}/auth.json; secrets writers, both import-host
endpoints, the CLI wizard symlink, the rollback snapshot list, install/
dev-setup seeds, and the host-import mkdir target all follow.

The assistant mount is repointed to config/stack/auth.json, and the guardian
now mounts the same file read-only at its OpenCode auth path
(/opt/openpalm/guardian/.local/share/opencode/auth.json), so provider
credentials are shared between assistant and guardian.

Tests and docs (mount tables, comments) updated to the new location.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Install the OpenCode binary in the guardian image (mirroring the assistant:
HOME=/usr/local install, pinned OPENCODE_VERSION=1.3.3, plus unzip/bash for
the installer). Guardian-side OpenCode instances now read their global config
from /etc/opencode, bind-mounted from OP_HOME/config/guardian (OPENCODE_CONFIG_DIR),
and share provider credentials with the assistant via the auth.json mount.

- channels.compose.yml: OPENCODE_CONFIG_DIR=/etc/opencode + config/guardian mount
- .openpalm/config/guardian/opencode.jsonc seeded (akm-opencode plugin, deny-read on auth.json)
- guardianConfigDir path helper; skeleton guardrail asserts the config ships
- docs (guardian README, mount tables) and the assistant Dockerfile comment updated

Verified by building the image: opencode --version → 1.3.3, resolves on PATH,
OPENCODE_CONFIG_DIR baked in, and config/guardian mounts at /etc/opencode.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Insert a semantic moderation stage between structural validation and the
assistant forward, so the guardian can inspect what an inbound message is
trying to do — not just that it is well-formed and signed.

Layered cheap → expensive (balances security and efficiency):
- channels-sdk/content-screen: pure in-process heuristics scoring prompt
  injection / jailbreak / exfiltration / obfuscation. Most traffic scores 0
  and never touches a model.
- guardian/moderation: messages over GUARDIAN_MODERATION_THRESHOLD escalate to
  the guardian's local OpenCode moderator (loopback :4097, started by the new
  entrypoint, small model pinned in config/guardian/opencode.jsonc, shared
  provider creds). Stateless ephemeral session per call; strict JSON verdict
  (allow / flag / block).

Fail-closed: an escalated message the moderator cannot classify (down, timeout,
unparseable) is blocked (403 content_blocked). Because that trades availability
for security, the whole stage is OFF by default (GUARDIAN_CONTENT_VALIDATION).

- config/guardian/instructions/moderation.md: taxonomy + output contract
- server.ts wiring + content_blocked error code + audit
- guardian entrypoint starts the loopback moderator when enabled
- compose env knobs; guardian README + skeleton guardrails

Tests: content-screen 16, moderation 15, server integration 2 (clean→200,
malicious→fail-closed 403). Full non-UI suite 705/705. Guardian image builds;
opencode 1.3.3 on PATH.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant