feat(http): a trusted caller on the tailnet #415
No reviewers
Labels
No labels
move-to-repo
coilyco-bridge-deploy
move-to-repo
coilyco-flight-deck-agent-compose
move-to-repo
coilyco-gaming-eco-app
move-to-repo
coilysiren-inbox
move-to-repo
unknown
🔒⚠️📦⚠️🔒 SANDBOXED 🔒⚠️📦⚠️🔒
autonomy
async-consult
autonomy
epic
autonomy
headless
autonomy
live-collab
c#
priority
P0
priority
P1
priority
P2
priority
P3
priority
P4
role/ai
role/creator
role/design
role/director
role/engineer
role/exec
role/human
role/ops
role/qa
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
coilyco-gaming/sirens-echo!415
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/a-trusted-caller-on-the-tailnet"
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?
closes #414
Identity half of #165, approved by Kai on 2026-08-12. 165 stays open for sessions and the principal assertion.
Safe by default
SIRENS_ECHO_HTTP_TOKENsupplies the token. Unset trusts nobody — exactly what this endpoint did before the token existed — so landing this changes nothing until a deployment decides otherwise.Constant-time comparison: a check that leaks its own answer through timing looks like a control and is not one.
The trap it avoids, which is the reason this PR exists at all
X-Sirens-Calleralready exists, is self-asserted, is used as a rate-limit key, and already flows into the turn's requester. It is a header named like an identity carrying a caller-supplied name.The smallest possible version of "add identity" is to trust it. That step treats an unauthenticated string as a principal, and it would pass every test anyone would think to write, because the plumbing works and the value arrives where expected.
So the trusted input is a different input. A test asserts the self-asserted header grants no trust and still partitions the rate limit, so neither property can drift.
Why identity had to come before sessions
A session is a handle to retained conversation. An endpoint accepting a session id from an unauthenticated caller and returning that history discloses conversations to whoever guesses an id.
history_count: 0is not merely a missing feature — it is why this endpoint is currently safe to expose without authentication.I was wrong twice on the way here, both recorded on 165
I declined this as "authentication on a network-facing endpoint" without checking what the endpoint faces. Echo is exposed through
ingress-tailscale;/v1/turnhas never been on the public internet. That does not make auth unimportant, it makes the threat model which tailnet peer — a bounded problem, and a different one from the one I talked myself out of.What trust does today
Nothing beyond a span attribute. That is deliberate: the value is that the distinction exists and is recorded, and every consumer of it is a separate decision. The prompt still asserts an HTTP caller is not the principal — making that conditional means rebuilding the system prompt per request, which is a cost and a design question rather than a line of code.
ward exec gategreen: build, policy-check, vet, test, test-skips, pre-commit.