Files
werkator/docs/prs/2026-09-10-PR#47-control-token-feedback.md
b57fc10a24 server: say when a control token is not accepted (#47)
Entering a wrong token looked exactly like entering none: the page came back
unchanged, so a typo, a token from the wrong repository and a rotated one were
indistinguishable from a broken button.

The page asks the server before it stores anything. A new endpoint answers 204
or 403 for a token in the header — the header, because this is asked before a
cookie exists and because a foreign page cannot set one — and the key reports a
refusal at once, naming where the right token lives, without storing it. A
stored token the server no longer accepts turns the key red instead of leaving
the page looking untouched.

The instance also logs the absolute path of its token file at startup: it is
relative to the service's working directory, which on an instance serving
several repositories is the one the systemd unit names and not any of the
others, and a token from the wrong one is silently just a wrong token.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: mhoennig <michael@hoennig.de>
Reviewed-on: #47
2026-09-10 07:27:28 +02:00

4.1 KiB

WARNING: This document describes only the change applied in this PR. It may already be outdated once the next PR is merged. Historic PR-documentation is not maintained along with new PRs — treat it as a snapshot, not as current documentation.

  • PR#43 — introduced the key in the header and the token cookie.

The Problem

Entering the control token has no visible effect when the token is wrong, and no way to tell that apart from a bug.

The page comes back exactly as it was: the actions stay grey, a private repository stays hidden, the key stays pale. That is the same picture as before entering anything, so a typo, a token copied from the wrong repository, or one that has been rotated all look identical — and identical to "the button is broken".

The wrong-repository case is easy to walk into. The token file is .git/werkator/control-token relative to the working directory of the service, which on an instance serving several repositories is the one its systemd unit names and not any of the others. Nothing said which one that is, and running werkator control-token in the wrong repository does not warn — it creates and prints a different token.

Non-Goals

  • No change to what the token protects, or to how it travels. It stays a header for anything that acts and a cookie for what may be seen.
  • No way to read the token out of the server. The new endpoint confirms a token that is already known; it never hands one out.

The Scenarios

Feature: telling a wrong token from no token

Scenario#47.01: The server answers whether a token is the right one

So that the page can say what is wrong before it stores anything.

  • Given the correct control token in the request header
  • When GET /api/control-token/verify is called
  • Then it answers 204 with an empty body
  • Given a wrong token, or none
  • Then it answers 403, alike, and says nothing further
  • And the answer is never stored by a cache
Verified by

Scenario#47.02: The key says what happened

  • Given a token entered at the key in the header
  • When the server refuses it
  • Then it is not stored, and a message says so and where the right one lives
  • When the server accepts it
  • Then it is stored and the page fetched afresh, in its operator form
  • Given a stored token that leaves the page in its visitor form
  • Then the key is red
    • and its tooltip says the token is not accepted when the server refuses it
    • and says the token is correct but is not arriving when the server accepts it — the cookie is being blocked or dropped on the way, which used to look exactly like a wrong token

Feature: finding the token

Scenario#47.03: The instance says where its token is

  • When the server starts
  • Then it logs the absolute path of its control-token file once

The Solution

ControlTokenApiController answers one question — is this the token — from the header only, with 204 or 403 and no body. The header, not the cookie, because the page asks before setting a cookie, and because a header cannot be sent by a foreign page.

The key button asks before storing anything. A refused token is reported at once, naming where the right one is, and is not stored, so nothing has to be undone. An accepted one is stored and the page fetched afresh, as before.

A token that was accepted once and is not any more used to be invisible; on load the key now compares "something is stored" with "the server trusts me" and turns red when the two disagree. It then asks the same endpoint with the header to tell the two causes apart, because a wrong token and a cookie that never arrives produce the identical page and need opposite reactions.

ServerConfiguration logs the resolved absolute path of the token file at startup, because "relative to the working directory" is not something an operator can see from the outside.

Additional Changes

  • Version 1.5.3 and its release-notes entry, since this is what gets deployed.