Goal: an independent control hub that launches AI coding sessions (Claude Code, Codex) across machines and handles instructions, approvals, status, and results from a single chat.
Direction (revised 2026-10-20, direction brief §0.2)
- Sessions started elsewhere (a terminal, Orca, ...) are observed only. Palantir does not send them instructions. Observation moves to the vendors' official hooks in SH2–SH3.
- Orca is an optional integration and is off by default. The core paths (observing sessions, status, instructions) work without Orca installed.
- Available today: observing sessions by reading each machine's Claude Code and Codex transcripts, and querying them from chat (session assistant). Not built yet: the session host that launches sessions and takes instructions from chat (SH1), and hooks collection (SH2–SH3). The order is SH0–SH5 in brief §0.2.10.
- The 3-tier delegation below (Top → Operator → Worker) is dormant. The code stays and is not deleted, but no new features go into it.
Main Manager (Top) ← oversees all projects and Operators
├── Operator (Project A) ← manages workers within the project
│ ├── Worker 1 ← performs actual coding tasks
│ ├── Worker 2
│ └── Worker 3
├── Operator (Project B)
│ └── Worker 1
└── Operator (Project C)
├── Worker 1
└── Worker 2
Worker — AI coding agents (Claude Code, Codex, etc.) that perform actual tasks within a project. Each worker runs in an isolated git worktree so they never collide.
Operator (Project Manager) — Assigned per-project to coordinate its workers. Distributes tasks, tracks progress, and maintains project-specific conventions and context for consistent direction.
Main Manager (Top) — The top-level orchestrator. Routes user instructions to the right Operator, manages priorities across projects, and serves as a single conversational entry point.
All layers are monitored and controlled from a single web dashboard (localhost:4177).
v3 Manager redesign shipped. See
docs/specs/manager-v3-multilayer.mdfor the spec anddocs/test-scenarios.mdfor the user-facing scenarios.
npm install
npm start
open http://localhost:4177docker compose up --build
open http://localhost:4177bash setup.sh # auto-detects and installs Node 20+, runs npm install
npm start# .env (Compose consumes this on the host; it is not copied into the image)
PALANTIR_TOKEN=my-secret-token
PALANTIR_PM_TOKEN=my-distinct-agent-token
ANTHROPIC_API_KEY=sk-ant-... # optional: for a Claude-backed Manager
CODEX_API_KEY=... # optional: for a Codex-backed Manager or Operator
docker compose up --build
# → http://localhost:4177 (send Authorization: Bearer my-secret-token)This Compose example enables authentication, but direct environment injection is reported as an unverified actor split unless the agent runtime is isolated from the Console process by the deployment boundary.
By default the server now binds to 127.0.0.1 (loopback only, no auth).
Setting PALANTIR_TOKEN automatically promotes the bind to 0.0.0.0 so remote
clients can reach an authenticated API. If you want to listen on all
interfaces without auth (discouraged), pass HOST=0.0.0.0 explicitly — the
server will log a [security] WARNING in that case.
| Setup | HOST |
PALANTIR_TOKEN |
Bind |
|---|---|---|---|
| Dev default | unset | unset | 127.0.0.1 |
| Remote + auth | unset | set | 0.0.0.0 |
| Legacy ("always 0.0.0.0") | 0.0.0.0 |
unset | 0.0.0.0 ( |
| Custom interface | 192.168.x.y |
either | as specified |
PALANTIR_TOKEN=my-secret-token npm start
# → all APIs require Authorization: Bearer my-secret-token (CLI) OR
# the palantir_token cookie set by POST /api/auth/login (browser).
# → binds to 0.0.0.0Browser clients use an HttpOnly cookie under the hood. Visit
http://host:4177/login.html, enter your token in the POST form, and
you'll be redirected to the console with a palantir_token cookie set.
The token never appears in a URL — earlier PR1 drafts accepted ?token=
as a one-shot bootstrap, but that was removed after Codex review because
the first document request would already leak the token into reverse
proxy access logs. This is required because EventSource cannot send
custom headers, so the /api/events SSE stream would otherwise be
blocked.
The browser authenticates with PALANTIR_TOKEN through the HttpOnly cookie.
Top/Operator and Worker processes never receive that global credential (or
PALANTIR_PM_TOKEN). When agents are isolated from one another and from the
Console by a real OS-user/container boundary, the server can issue boot-local,
run-bound capabilities.
Manager capabilities work only while that exact manager slot is active and only
on the documented orchestration endpoints. Their values are delivered through
the manager-only PALANTIR_MANAGER_TOKEN process environment and never rendered
into argv or the persisted system-prompt file. Worker capabilities are accepted
only by that run's candidate-only memory proposal endpoint. PALANTIR_PM_TOKEN
is optional and retained as a distinct bearer credential for trusted external
automation; it is not injected into agents.
Authenticated installs default to agent_capabilities_disabled. A same-UID
process can inspect another process's environment (or temporary launch state),
so the built-in local tmux/subprocess topology is not a capability boundary.
Set PALANTIR_AGENT_PROCESS_ISOLATION=verified only when the deployment
actually runs every capability-bearing agent behind a separate OS account or
container boundary that prevents peer and Console-process inspection. Without
that explicit attestation, managers receive no orchestration credential and
workers receive no memory-proposal credential. This is fail-closed; browser and
trusted external bearer access continue to work.
Run npm run diagnose:isolation (or
npm run --silent diagnose:isolation -- --json for clean automation output)
to see the exact policy inputs that currently block run-bound capabilities. A
non-ready result exits with status 2. The command checks the runtime gate; it
does not prove that an OS/container boundary exists. See the Korean
isolated capability runbook before
making the verified attestation.
PALANTIR_ACTOR_TOKEN_FILE 로 기동하는 경우 이 명령은 판정하지 못한다(종료 코드 3). 그 파일은 부팅 때 한 번 소비되어 애플리케이션 옵션으로 넘어가므로 환경변수로 존재하지 않는다 — 실패가 아니라 "여기서는 알 수 없음" 이다. 종료 코드 2 만 실제 미충족을 뜻한다.
Local installs must not store either global token in the repository
.env (startup rejects that configuration). For the secure path, create a
mode-0600, non-symlink JSON file containing PALANTIR_TOKEN and optionally
PALANTIR_PM_TOKEN, then start with
PALANTIR_ACTOR_TOKEN_FILE=/path/to/file; the Console validates ownership,
reads it once, and unlinks it before spawning agents. This verifies token
bootstrap, independently from the required agent process-isolation attestation.
Direct environment variables remain supported for compatibility but are
reported as an unverified token-source boundary because an unsandboxed CLI may
be able to inspect its parent environment. Use an OS/container boundary if the one-shot
file itself cannot be protected until startup. Windows currently rejects the
one-shot file transport because Node's portable filesystem API cannot verify
its DACL; use application-owned options or an OS/container secret boundary
there, which remains reported as unverified unless the embedder marks it.
{"PALANTIR_TOKEN":"human-secret","PALANTIR_PM_TOKEN":"agent-secret"}Save that JSON outside the repository with mode 0600, then pass only its path
from the launcher environment:
PALANTIR_ACTOR_TOKEN_FILE=/secure/ephemeral/palantir-actor-tokens.json npm startFor an executor that genuinely provides per-agent isolation:
PALANTIR_ACTOR_TOKEN_FILE=/secure/ephemeral/palantir-actor-tokens.json \
PALANTIR_AGENT_PROCESS_ISOLATION=verified \
npm startWhen enabling the split on an existing installation, rotate PALANTIR_TOKEN
in the same restart. This revokes the value inherited by any legacy or remote
worker that predates the split; local orphan workers are also stopped instead
of reattached when they lack the credential-policy marker. Project workers with
a run-bound memory proposal grant are also stopped on Console restart because
that boot-local grant cannot be rewritten in a live process; safely scrubbed
project-less workers remain recoverable. The same rotation is recommended when
upgrading an authenticated shared-token installation that may still have
workers launched by an older version.
For SSH/fleet workers, set PALANTIR_BASE_URL to a Console URL reachable from
the remote node. Without it, remote workers intentionally receive no memory
proposal token or loopback URL; local workers derive a reachable URL from
HOST and PORT.
Main Manager (Top) → Operator (per project) → Worker (per task)
(orchestrator) (project manager) (AI coding agent)
| Concept | Description |
|---|---|
| Main Manager (Top) | Top-level orchestrator. Routes user instructions to the right Operator, oversees all projects |
| Operator (Project Manager) | Per-project manager. Coordinates workers, distributes tasks, maintains project context. Lazy-spawned on first message |
| Worker | AI agent (Claude Code or Codex) that executes a task. Runs in an isolated git worktree |
| Project | Logical grouping of tasks. e.g. "Backend API", "Frontend Refactor" |
| Task | A unit of work. Managed on a kanban board. Status: Backlog → Todo → In Progress → Review → Done |
| Run | An agent execution against a Task. Multiple Runs per Task allowed |
| Agent Profile | Agent configuration (Claude Code, Codex CLI, custom) |
| Conversation | 1st-class identity for any chat surface: top, operator:<projectId> (pm:<projectId> accepted), or worker:<runId> |
Control hub. Shows only what needs attention right now — active agents, needs-input, failures, and the new Drift ⚠ badge.
- Drift badge — summary of Operator dispatch-audit incoherences (annotate-only; hidden when zero). Click to open the Drift Drawer with per-row Operator claim vs DB truth diffs, dismiss/restore, and kind color bar.
Kanban board. Drag tasks across 5 columns. Drag to In Progress → agent execution modal opens.
Project list. Tasks are grouped by project. Each project exposes:
pm_enabled— whether the project can lazy-spawn an Operatorpreferred_pm_adapter— Codex or Claude preference (Claude resume falls back to Codex until Phase 3b)project_brief— conventions + known pitfalls injected into Operator system prompt
Central orchestrator. Runs a manager agent (Claude Code or Codex CLI) with multi-turn chat and v3 multi-layer routing.
- 40/60 split layout: chat (left) + worker session grid (right)
- Conversation target dropdown (v3 Phase 6) — lets the user send messages to the Top session or any
operator:<projectId>slot (pm:<projectId>is still accepted). Operator rows are markedactivewhen an Operator is currently spawned.@<projectName>in a message is rewritten to the matching Operator via/api/router/resolve. - Reset Operator button — disposes the Codex thread and clears the persisted
pm_thread_id; the next message starts a fresh thread. - Per-Operator drift indicator (v3 Phase 7) —
⚠ Nbutton next to Reset Operator when the selected Operator has pending incoherent audit rows. - Manager queries the Palantir Console REST API via curl to report real agent/task status.
- Supports Claude Code CLI (
--print --output-format stream-json --input-format stream-jsonprotocol) and Codex CLI (codex exec --json+ resume viacodex exec resume <thread_id>).
Agent profile management. Default profiles: Claude Code and Codex CLI. Agent profiles can gate dispatch via capabilities_json and max_concurrent.
Consolidated tab group (nav-consolidation). Three sub-tabs:
- Skill Packs (
#resources/skills, formerly#skills) — MCP server templates and skill pack management. Browse the gallery registry, install from registry or URL, import/export as JSON. Packs can be bound to projects, tasks, and runs. - Presets (
#resources/presets, formerly#presets) — Worker Preset management. Bundle agent profile + plugin refs + MCP server templates + system prompt into a reusable preset. Link presets to tasks viapreferred_preset_id. Run Inspector shows frozen snapshot vs current preset drift. - MCP Servers (
#resources/mcp-servers, formerly#mcp-servers) — MCP server template CRUD UI (M3-UI, PR #119). Add / edit / delete the templates that Skill Packs and Presets reference by alias. Templates carrycommand,args,env,allowed_env_keys, andupdated_at(migration 020) — RunInspector compares the snapshot'supdated_atagainst the current template to surface body-level drift even though the snapshot only stores the template id.
Legacy hashes
#skills,#presets,#mcp-serversredirect to their canonical sub-routes via the app.js redirect map.
Consolidated tab group (nav-consolidation). Main sub-tabs:
- Operator Roster (
#operator) — create durable Operator instances, map codebases, manage schedules, and select each instance's CLI (Codex,Claude Code, or automatic fallback). Changing CLI preserves the Operator identity/profile/mappings/schedules and resets only its runtime conversation thread. - Codebases (
#operator/codebases) — inspect and manage Operator codebase placement. - Operator Profiles (
#operator/profiles, formerly#operator-profiles) — Operator profile CRUD (PF-1/PF-2). - Specialist (
#operator/specialist) — Invoke a folder-less specialist worker against an active manager run.
Light mode launched in PR #150~#153 (K-2). The NavSidebar bottom toggle is 3-state — ◑ system (auto via prefers-color-scheme) / ☀ light / ☽ dark — and the choice is stored in localStorage.palantir.theme. server/public/theme-init.js runs in <head> before tokens.css to set <html data-theme> and patch <meta name="theme-color"> so there is no FOUC and the mobile chrome color follows the active theme.
REST API for external control. When auth is enabled, include Authorization: Bearer <token> header.
GET /api/projects — list
POST /api/projects — create { name, directory?, color?, pm_enabled?, preferred_pm_adapter? }
GET /api/projects/:id — get
PATCH /api/projects/:id — update
DELETE /api/projects/:id — delete (fail-closed: aborts if operatorCleanupService cannot dispose a live Operator)
GET /api/projects/:id/tasks — list tasks
GET /api/projects/:id/brief — read project brief (conventions + known pitfalls)
PATCH /api/projects/:id/brief — partial update { conventions?, known_pitfalls? }
GET /api/tasks — list (?project_id=, ?status=)
POST /api/tasks — create { title, project_id?, priority?, description?, task_kind?, requires_capabilities?, acceptance_criteria? }
GET /api/tasks/:id — get
PATCH /api/tasks/:id — update
PATCH /api/tasks/:id/status — change status { status }
DELETE /api/tasks/:id — delete
PATCH /api/tasks/reorder — reorder { orderedIds: [] }
POST /api/tasks/:id/execute — run agent { agent_profile_id, prompt? }
GET /api/operator-instances — list durable Operator instances
POST /api/operator-instances — create { profile_id, display_name?, primary_project_id?, preferred_adapter? }
PATCH /api/operator-instances/:id/adapter — switch CLI { preferred_adapter: "codex" | "claude" | null }; resets only the runtime thread
GET /api/runs — list (?task_id=, ?status=)
GET /api/runs/:id — get
GET /api/runs/:id/events — event list
GET /api/runs/:id/output — live output (tmux)
POST /api/runs/:id/input — send input { text } (delegates to conversationService, same parent-notice router)
POST /api/runs/:id/cancel — cancel
DELETE /api/runs/:id — delete
GET /api/agents — list profiles
POST /api/agents — create { name, type, command, args_template?, max_concurrent?, capabilities_json?, env_allowlist? }
GET /api/agents/:id — get (+ runningCount)
GET /api/agents/:id/usage — provider-backed usage snapshot for this profile
PATCH /api/agents/:id — update
DELETE /api/agents/:id — delete
POST /api/manager/start — start the Top manager { prompt?, cwd?, model?, agent_profile_id? }
POST /api/manager/message — send to Top (delegates to conversationService.sendMessage('top', ...))
GET /api/manager/status — { active, run, usage, claudeSessionId, top: {...}, pms: [...] }
GET /api/manager/events — Top event list (incremental via ?after=<id>)
GET /api/manager/output — Top output text
POST /api/manager/stop — stop Top (also clears pending parent-notice queue for Top's runId)
POST /api/manager/pm/:projectId/message — lazy-spawn Operator if needed, then send
POST /api/manager/pm/:projectId/reset — single-owner teardown: dispose adapter, cancel run, clear pm_thread_id, drop registry slot
GET /api/conversations/:id — resolve { conversation: { kind, conversationId, run? } }
POST /api/conversations/:id/message — send { text, images? } — `id` is 'top' | 'operator:<projectId>' | 'worker:<runId>' (`pm:<projectId>` accepted)
GET /api/conversations/:id/events — event list (incremental ?after=<id>)
POST /api/router/resolve — { text, currentConversationId?, defaultConversationId? }
→ { target, text, matchedRule, ambiguous?, candidates? }
POST /api/dispatch-audit — record a Operator claim
{ project_id, task_id?, pm_run_id?, selected_agent_profile_id?, rationale?, pm_claim: { kind, task_id? / run_id? } }
→ 201 { audit: { ..., incoherence_flag, incoherence_kind } }
GET /api/dispatch-audit — list (?project_id=, ?incoherent_only=1, ?limit=<1..500>)
Supported pm_claim.kind values: task_complete, task_in_progress, worker_spawned, worker_running, worker_completed, worker_failed. Unknown kinds are stored with incoherence_flag=0, incoherence_kind='unknown_kind' so later matchers can widen without rewriting history.
GET /api/sessions — list legacy OpenCode sessions
GET /api/sessions/:id — read a single session (with messages)
POST /api/sessions — create a new session
POST /api/sessions/:id/message — append a message { content }
PATCH /api/sessions/:id — rename
DELETE /api/sessions/:id — move to trash
GET /api/trash/sessions — list trashed sessions
POST /api/trash/sessions/:trashId/restore — restore a trashed session
DELETE /api/trash/sessions/:trashId — permanent delete
GET /api/fs — directory browse (?path=)
GET /api/usage/providers — aggregated provider usage (Codex / Anthropic / …)
GET /api/usage/codex-status — Codex-specific connection/auth status
GET /api/claude-sessions — active Claude Code subprocesses (Manager + workers)
GET /api/events — SSE stream
GET /api/health — health check
GET /api/worker-presets — list
POST /api/worker-presets — create
GET /api/worker-presets/:id — get
PATCH /api/worker-presets/:id — update
DELETE /api/worker-presets/:id — delete (app-level cascade: tasks.preferred_preset_id → NULL)
GET /api/worker-presets/plugin-refs — available plugin directories
GET /api/mcp-server-templates — list
POST /api/mcp-server-templates — create { alias, command, args?, env?, allowed_env_keys? }
GET /api/mcp-server-templates/:id — get
PATCH /api/mcp-server-templates/:id — update (bumps `updated_at` so RunInspector can show drift vs preset snapshot)
DELETE /api/mcp-server-templates/:id — delete (rejected if a preset references it)
GET /api/skill-packs — list installed (MCP server templates)
POST /api/skill-packs — create template
GET /api/skill-packs/:id — get
PATCH /api/skill-packs/:id — update
DELETE /api/skill-packs/:id — delete
GET /api/skill-packs/:id/export — export as JSON
POST /api/skill-packs/import — import from JSON
GET /api/skill-packs/templates — list templates
GET /api/skill-packs/registry — browse gallery registry
GET /api/skill-packs/registry/pack — get single pack detail
POST /api/skill-packs/registry/install — install from registry
POST /api/skill-packs/registry/install-url — install from URL (SSRF-guarded)
POST /api/skill-packs/registry/update — update installed pack
POST /api/skill-packs/registry/refresh — refresh registry cache
Project/Task/Run bindings (mounted via skillPacks.projectBindings / taskBindings / runSnapshots):
GET /api/projects/:id/skill-packs — project's skill packs
POST /api/projects/:id/skill-packs — bind pack to project
PATCH /api/projects/:id/skill-packs/:packId — update binding
DELETE /api/projects/:id/skill-packs/:packId — unbind
GET /api/tasks/:id/skill-packs — task's skill packs
POST /api/tasks/:id/skill-packs — bind pack to task
DELETE /api/tasks/:id/skill-packs/:packId — unbind
GET /api/runs/:id/skill-packs — run's resolved packs
PATCH /api/runs/:id/skill-packs/checks — update pack checks
API reference completeness: the sections above enumerate every route mounted by
server/app.jsat Phase 10G merge. When adding a new route, mount it inapp.jsAND add it to this section — otherwise this file silently drifts out of sync with the server.
| Channel | Meaning | v3 semantic envelope fields |
|---|---|---|
task:created / task:updated / task:deleted |
Task mutations | — |
run:created |
New run row | — |
run:status |
Any run status transition (incl. createRun normalized emit) |
from_status, to_status, reason, task_id, project_id |
run:ended |
Terminal transition | same as run:status |
run:completed |
Agent exit captured in health loop | same + reason='agent-exit-success' | 'agent-exit-error(N)' |
run:needs_input |
Priority alert — idle timeout detected | same + priority: 'alert' |
run:event |
Per-vendor raw event (high volume) | — |
manager:started / manager:stopped |
Top manager lifecycle | — |
dispatch_audit:recorded |
New Operator dispatch claim audited | audit, project_id, pm_run_id, incoherence_flag, incoherence_kind |
Client pattern: the Drift badge + drawer and run:needs_input tab-title pulse are both wired through these semantic fields. run:status is a pure reload trigger and does NOT fire priority notifications — that responsibility belongs to the dedicated channels (run:needs_input, run:completed) to avoid duplicate alerts.
Worker Presets bundle a system prompt, MCP server set, and (optionally)
host-isolated plugin directories so a worker spawn is reproducible across
machines. Spec: docs/specs/worker-preset-and-plugin-injection.md.
- You want a Claude worker that loads ecosystem plugins (e.g.
agent-olympus) without polluting your host~/.claude/. - You want to enforce a specific MCP server set per task class.
- You want forensic snapshots of "exactly what plugin files / prompt / MCP config the worker ran with" for past runs.
-
Drop a Claude Code plugin under
server/plugins/<name>/with aplugin.jsonat its root. Example:server/plugins/agent-olympus/ plugin.json skills/ commands/ agents/The directory is gitignored — operators place plugins by hand. The CI suite uses fixtures under
server/tests/fixtures/plugins/. -
Open the Presets view (
#resources/presets) and create a preset. Fields:- Plugin Refs — one or more directory names from
server/plugins/. - MCP Server Templates — pick from existing
mcp_server_templatesentries (managed by the Skill Pack subsystem). - Base System Prompt (≤16KB) — prepended to skill-pack sections.
- Isolated — Tier 2 (Claude only). Adds
--bare,--strict-mcp-config,--setting-sources, and one--plugin-dirflag per ref to the worker spawn. Non-Claude workers get apreset:tier2_skippedwarning and continue with Tier 1 only. - Min Claude Version — semver gate enforced before spawn.
- Setting Sources — passed as
--setting-sources <value>. Default empty disables host setting inheritance entirely.
- Plugin Refs — one or more directory names from
-
Set the preset on a Task (the
Worker Presetdropdown inExecute Task). The dropdown defaults to the task'spreferred_preset_id. Operator can override per-execute. -
After the run finishes, open
Run Inspector → Presetto see the frozen snapshot, the current preset row, and any drift between them.
--bare Claude ignores OAuth and the macOS keychain. By default Palantir
does not read, store, forward, or convert Claude OAuth credentials, so an
isolated preset only authenticates with an actual ANTHROPIC_API_KEY (not an
sk-ant-oat* OAuth token) or a cloud-provider auth mode; otherwise it fails
closed with a 400 + diagnostic. PALANTIR_LEGACY_CLAUDE_CREDENTIAL_ACCESS=1
restores the old materialization (env → .claude-auth.json → keychain
claudeAiOauth.accessToken, written to a temporary apiKeyHelper).
run_preset_snapshots rows are written at preset-resolve time (before
spawn), keyed by run_id with <pluginRef>/<relpath> namespaced file
hashes. Deleting a preset later does not erase past snapshot rows;
tasks.preferred_preset_id is set NULL via app-level cascade.
Codex manager sessions launched through codexAdapter always run with
--dangerously-bypass-approvals-and-sandbox; there is no environment-variable
switch for this behavior. Managers need network access to call the Palantir
Console API, and the --full-auto sandbox blocks that access, making
orchestration impossible. Codex task workers use the separate lifecycle worker
path: their approval and sandbox flags come from the profile's editable
args_template. The default exec {prompt} profile does not add the bypass
flag.
| Variable | Default | Description |
|---|---|---|
PORT |
4177 |
Server port |
PALANTIR_TOKEN |
(none) | Enables Bearer/cookie auth and promotes bind from 127.0.0.1 to 0.0.0.0 |
PALANTIR_PM_TOKEN |
(none) | Optional distinct bearer credential for trusted external automation; never injected into agent processes |
PALANTIR_ACTOR_TOKEN_FILE |
(none) | POSIX only: mode-0600 one-shot JSON containing the two actor tokens; consumed and unlinked before agent startup (Windows fails closed because DACLs cannot be verified portably) |
PALANTIR_AGENT_PROCESS_ISOLATION |
(none) | Set to verified only when every capability-bearing agent is isolated by a separate OS account or container; otherwise run-bound agent capabilities stay disabled |
PALANTIR_GIT_ENV_ALLOWLIST |
(none) | Additional node-local environment variable names inherited by Git operations (comma-separated), for example an askpass helper's companion credential |
PALANTIR_PROJECT_TEST_ENV_ALLOWLIST |
(none) | Additional node-local environment variable names inherited by materialized project test commands (comma-separated), for example PYTHONPATH |
HOST |
auto | Override the bind address. 0.0.0.0 without a token logs a security warning |
PALANTIR_ALLOWED_COMMANDS |
(none) | Additional allowed CLI commands (comma-separated) |
PALANTIR_DEFAULT_PM_ADAPTER |
codex |
Global Operator adapter fallback when an instance uses Auto and its project has no preferred_pm_adapter |
ANTHROPIC_BASE_URL / CLAUDE_CODE_OAUTH_TOKEN / ANTHROPIC_API_KEY |
— | Claude Code auth. By default OAuth values (CLAUDE_CODE_OAUTH_TOKEN, sk-ant-oat* keys) are removed at boot and never forwarded; the claude CLI reads its own login. An actual API key is still used |
PALANTIR_LEGACY_CLAUDE_CREDENTIAL_ACCESS |
(off) | Exactly 1 restores legacy Claude credential handling (.claude-auth.json save/restore, token materialization for --bare/isolated/distiller, usage lookup). Not an SH1-supported configuration |
CODEX_API_KEY / OPENAI_API_KEY |
— | Codex auth (preflight checks ~/.codex/auth.json) |
CODEX_BIN |
codex |
Codex CLI path |
CODEX_HOME |
~/.codex |
Codex config home |
OPENCODE_STORAGE |
~/.local/share/opencode/storage |
OpenCode session storage path |
OPENCODE_BIN |
opencode |
OpenCode binary path |
OPENCODE_FS_ROOT |
$HOME |
Directory picker root path |
- Backend: Express.js 5, SQLite (WAL mode, better-sqlite3), EventEmitter SSE
- Frontend: Preact + HTM (ESM + UMD, no build step), hash router, self-hosted Inter font
- Worker agents: tmux session + git worktree isolation
- Manager agents: Claude Code CLI (stream-json NDJSON) OR Codex CLI (
codex exec --json+ thread resume), selected per agent profile - Real-time: SSE (Server-Sent Events) with
Last-Event-IDreplay - Tests: Node.js built-in test runner + supertest (902 unit/integration) + Playwright e2e (smoke + manager + a11y 32 + visual 32) at PR #169, 2026-04-29
npm test # run tests (902 unit/integration)
npm run test:e2e # Playwright e2e (smoke + manager + a11y + visual)
npm run test:a11y # axe-core a11y only (K-4, 32 scenarios — 8 routes × 2 themes × 2 viewports)
npm run test:visual # Playwright screenshot diff (K-5, same 32 scenarios)
npm run dev # dev serverData is stored in palantir.db (SQLite). Auto-migrated on server start (server/db/migrations/001..021_*.sql; the latest two are 020_mcp_template_updated_at.sql and 021_skill_pack_origin_type_check.sql).
See also:
docs/specs/manager-v3-multilayer.md— the v3 redesign spec (lock-in + phase history)docs/specs/worker-preset-and-plugin-injection.md— Phase 10 Worker Preset specdocs/specs/skill-packs.md— Skill Pack specdocs/specs/light-theme-k2-brief-2026-04-28.md— K-2 light mode launch brief (LAUNCHED stamp)docs/handoff-post-k2-launch-2026-04-29.md— UI/UX cleanup + K-2 launch series handoffdocs/test-scenarios.md— QA scenarios (PRJ,TSK,BRD,RUN,INS,MGR,Operator,DRIFT,ROUTER,SSE,REG,PRESET, …)CLAUDE.md— project conventions + autonomous-mode working style
ISC