Conversation
…rsation `eesel support read --task <task_id> <read command…>` runs one ordinary read command against a CUSTOMER's workspace. The server mints the credential: POST /support/mint-readonly-token exchanges a support conversation for a 15-minute, read-only token scoped to the workspace owned by that conversation's verified sender. The support side never names the target, so neither the model nor anything the customer wrote can steer it. Also `eesel support status` (live sessions and time left) and `eesel support end` (drop the cached tokens). How it hangs off the existing CLI, deliberately narrowly: - `require_creds()` returns the minted creds while a session is armed, so every existing read command targets the customer with no change to itself. - Those creds carry `ephemeral`, which `save_creds` already refuses to persist, so a support read can never become the ambient identity or overwrite the operator's own login. The token is cached in its own 0600 store (~/.config/eesel/support/), which nothing outside `support` reads. - The mint's four documented refusals get named, actionable messages: `no_verified_sender` and `sender_not_verified_owner` are expected outcomes for plenty of conversations, and have to read as answers, not as a broken CLI. Writes: the SERVER is the boundary (it rejects every mutating method on a read-only token). The three client layers here just make the refusal legible before the call: a read-noun allowlist (`chat`/`new`/`login` never run here), the parser's own `write=True` tags, and a backstop on any mutating request the first two missed. Known gap, server-side: `tasks list`/`count`/`analytics` are reads the API serves over POST, and the read-only gate is method-based, so it blocks them. `tasks show <id>` (GET) works. Reading a customer's activity list needs an explicit exemption for /workspace/tasks and /workspace/tasks/analytics. Server contract + design: agents/support/CLI-HANDOFF.md and agents/support/READONLY-IMPERSONATION.md on eeselapp/slack branch claude/eesel-support-agent-f45a70. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The CLI half of read-only support impersonation. The server half is built and
reviewed on
eeselapp/slackbranchclaude/eesel-support-agent-f45a70(
agents/support/CLI-HANDOFF.md,agents/support/READONLY-IMPERSONATION.md).What it does
POST /support/mint-readonly-tokenexchanges a support conversation for a15-minute, read-only token scoped to the workspace owned by that conversation's
verified sender. The support side never names the target, so neither the model
nor anything the customer wrote can steer it.
How it hangs off the existing CLI
Deliberately narrow, per the handoff's scope constraint (assume the CLI works;
don't refactor it). Four one-line hooks in shared code, everything else is one
contiguous new block:
require_creds()returns the minted creds while a session is armed, so everyexisting read command targets the customer with no change to itself.
ephemeral, whichsave_credsalready refuses to persist,so a support read can never become the ambient identity or overwrite the
operator's own login. The token is cached in its own 0600 store
(
~/.config/eesel/support/) that nothing outsideeesel supportreads, andis never printed.
advisory line on a 401/403.
Writes: the server is the boundary. The three client layers just make the
refusal legible before the call - a read-noun allowlist (
chat/new/loginnever run here), the parser's own
write=Truetags, and the backstop.Verified live
Against a branch env of the server branch (
claude-eesel-support-agent-f45a70):caller_not_allowlisted,task_not_in_support_workspaceandno_verified_senderall reproduced end to end through the CLI, each with itsnamed
reason:line and exit 3.agents list,instructions,files list,integrations list,automations schedules list,skills list,workspace show,billing show usage,settings notifications show, plus--json/--fields/--agentpassthrough.cache re-mints.
email-verified workspace owner, and a branch env's ephemeral DB has none. The
post-mint half was verified with a token identical to what the server mints.
50 new tests; full suite 1101 passing.
Known gap, server-side (not fixed here)
tasks list/count/analyticsare reads the API serves overPOST, andthe read-only gate is method-based, so it refuses them - confirmed live
(
[read-only-token] refused POST /workspace/tasks).tasks show <id>(GET)works. Reading a customer's activity, which the design names as a goal, needs
an explicit server-side exemption for
/workspace/tasksand/workspace/tasks/analytics.🤖 Generated with Claude Code