Refuse writes to the live config from someone who does not hold the design.
The design bar already refuses to autosave without the checkout, and
``/api/engine/documents/{id}/autosave`` already 423s a non-holder
(``stardesign.documents``). So why gate the config routes too?
Because between those two points sits the session config, and *that* is what the
UI shows you. Without this, a read-only viewer's edits land in their own session,
the screen updates, and nothing ever tells them the design did not change --
the edit simply evaporates at the next reload. Refusing at the point of the edit
is what turns a silent loss into an error.
This is a correctness guard, not a security boundary. The session config is
per-user, so a write here can only ever affect the caller's own view; the
boundary that protects the shared design is the checkout on /autosave.
Which design the caller has open is not something the backend tracks -- the bar
owns that -- so the client names it, either in the ``X-Design-Owner`` /
``X-Design-Id`` headers or, for the optimizer layers, in ``design_owner`` /
``design_id`` query parameters. Both spellings exist because the layer runs are
consumed with ``EventSource``, which cannot set request headers at all.
When no design is named there is nothing to protect and the write is allowed:
the config routes long predate designs, and the file-upload and scripted paths
still use them with no design open at all.