Files
stack/packages/mosaic
Hermes Agent 06046f7675
ci/woodpecker/pr/ci Pipeline was successful
wrapper-guard: close three fail-opens the last round left, and enumerate the large-repo test
Round-two remediation of the four blockers gate-ultron-01 raised on f8d04d1b. All
four were confirmed by my own measurement before being touched; none is taken on
the reviewer's word.

1. The --config/-K refusal never ran. It sat nested inside `if API_SHAPED`, and
   API_SHAPED is a test for a provider URL in the command text — which is exactly
   what a config file removes. The check was guarded by the condition that the
   capability it guards against defeats, so `curl --config /tmp/write.cfg` walked
   past it. It now keys on curl itself, ahead of the URL gate, and covers the
   attached (`-K/tmp/f`) and bundled (`-sK`) spellings a space-separated test
   cannot see.

2. Percent-encoded endpoints are a live route, not a theoretical one. Measured
   against the provider: `…/issues/1174` and `…/iss%75es/1174` both return HTTP
   200 for the same object. A write carrying any percent-escape is now refused
   rather than decoded — a decoder has to be exactly right about depth
   (%2569 -> %69 -> i) and about the provider's own normalisation, and being
   approximately right there is indistinguishable from not checking. Scoped to
   writes: a read is never this hook's business and a query string carrying %20
   is an ordinary URL.

3. HOME was still expanded unguarded at the `W=` fallback, which runs before any
   of the new HOME adjudication — so a guard deployed without its siblings still
   died on an unset HOME, upstream of the fix that was supposed to survive it.
   Moving a fail-open earlier in the file is not closing it. HOME is now resolved
   once, above every use, and every later site reads the resolved value.

   The existing harness could not have caught this: it runs the guard beside its
   siblings, so `[ -x "$W/pr-review.sh" ]` always succeeded and the fallback was
   never reached. A test's blind spot can be a property of the harness rather
   than of the code. The new lone_case() block copies the guard alone into an
   empty directory and re-asserts the four behaviours there.

4. test-mosaic-worktree-large-repo.sh shipped at mode 100644 and appeared in no
   CI step, so the enumeration guard (#1017) redded pipeline 2386 — correctly.
   Committed mode is now 100755 and the test is enumerated in the sanitization
   step. My own process miss: I verified the CI queue before pushing and never
   verified terminal CI after.

Controls: the 25 fixtures added here all FAIL against 029af418 (rc 0 or 1 where 2
is required) and all pass at this head, 143/143.
2026-08-13 02:18:35 -05:00
..

@mosaicstack/mosaic

CLI package for the Mosaic self-hosted AI agent platform.

Usage

mosaic wizard           # First-run setup wizard
mosaic gateway install  # Install the gateway daemon
mosaic config show      # View current configuration
mosaic config hooks list  # Manage Claude hooks

Headless / CI Installation

Set MOSAIC_ASSUME_YES=1 (or ensure stdin is not a TTY) to skip all interactive prompts. The following environment variables control the install:

Gateway configuration (mosaic gateway install)

Variable Default Required
MOSAIC_STORAGE_TIER local No
MOSAIC_GATEWAY_PORT 14242 No
MOSAIC_DATABASE_URL (none) Yes if tier=team
MOSAIC_VALKEY_URL (none) Yes if tier=team
MOSAIC_ANTHROPIC_API_KEY (none) No
MOSAIC_CORS_ORIGIN http://localhost:3000 No

Admin user bootstrap

Variable Default Required
MOSAIC_ADMIN_NAME (none) Yes (headless)
MOSAIC_ADMIN_EMAIL (none) Yes (headless)
MOSAIC_ADMIN_PASSWORD (none) Yes (headless)

MOSAIC_ADMIN_PASSWORD must be at least 8 characters. In headless mode a missing or too-short password causes a non-zero exit.

Example: Docker / CI install

export MOSAIC_ASSUME_YES=1
export MOSAIC_ADMIN_NAME="Admin"
export MOSAIC_ADMIN_EMAIL="[email protected]"
export MOSAIC_ADMIN_PASSWORD="securepass123"

mosaic gateway install

Runtime launchers

mosaic claude            # Launch Claude Code with Mosaic injection
mosaic yolo claude       # …with --dangerously-skip-permissions
mosaic codex | opencode | pi

mosaic claudex (EXPERIMENTAL)

Runs GPT models inside the Claude Code harness by pointing Claude Code at a local claude-code-proxy that translates the Anthropic Messages API to a ChatGPT-subscription (Codex OAuth) backend. This is not Anthropic Claude — model behavior, tool use, and output quality may differ. Intended for evaluation, not production delivery.

mosaic claudex           # launch (prompts through the proxy readiness gate)
mosaic yolo claudex      # …with --dangerously-skip-permissions
mosaic claudex --print "hello"   # trailing args are forwarded to Claude Code

Prerequisite: the claude-code-proxy binary must be installed and authenticated (claude-code-proxy codex auth …). mosaic claudex runs a preflight that verifies the binary, the OAuth state (triggering a device re-auth if needed), and a trusted local listener before launching; it fails closed if the proxy cannot be brought up with a verified identity.

Isolation (never touches your real Claude state). claudex always launches against an isolated CLAUDE_CONFIG_DIR (default ~/.config/mosaic/claudex/home). The ambient CLAUDE_CONFIG_DIR is deliberately ignored, and a guard proves the resolved dir can never be — or live under — the real ~/.claude. A claudex session therefore cannot mutate your normal Claude Code config.

No token leakage. claudex never reads the proxy's credential file. Claude Code is handed only ANTHROPIC_AUTH_TOKEN=unused pointed at the loopback proxy; the entire credential-bearing env family (ANTHROPIC_*, AWS_*, GOOGLE_CLOUD_*, GOOGLE_APPLICATION_CREDENTIALS, *_TOKEN, *_KEY, *_SECRET, …) is stripped from the composed environment. The Bedrock/Vertex routing switches (CLAUDE_CODE_USE_BEDROCK, CLAUDE_CODE_USE_VERTEX, and the _SKIP_*_AUTH pair) are force-removed regardless of value — otherwise their mere presence would route Claude Code to the real Anthropic API via AWS/GCP and bypass the proxy. The proxy holds the real OAuth credential.

Model tiers (override via env).

Tier Env var Default
primary (opus/sonnet) ANTHROPIC_MODEL gpt-5.6-sol
small/fast (haiku) ANTHROPIC_SMALL_FAST_MODEL gpt-5.6-luna

Operator-provided values win over the defaults. Additional overrides: MOSAIC_CLAUDEX_CONFIG_DIR (isolated config dir), ANTHROPIC_BASE_URL (proxy endpoint).

Hooks management

After running mosaic wizard, Claude hooks are installed in ~/.claude/hooks-config.json.

mosaic config hooks list              # Show all hooks and enabled/disabled status
mosaic config hooks disable PostToolUse  # Disable a hook (reversible)
mosaic config hooks enable PostToolUse   # Re-enable a disabled hook

Set CLAUDE_HOME to override the default ~/.claude directory.