diff --git a/.loom/cart/current/claude-marmalade.md b/.loom/cart/current/claude-marmalade.md index e09c3ca..d616486 100644 --- a/.loom/cart/current/claude-marmalade.md +++ b/.loom/cart/current/claude-marmalade.md @@ -240,3 +240,120 @@ self-hosted gitea or gitlab may not, and **git's fallback is silent** — it war **`--depth=1` bounds the damage to one commit**, *which is why both flags are there and not just the filter.* **Detect the warning and say so**, rather than letting `list` quietly become a full clone of somebody's monorepo. + +--- + +# The token, and `published check` was wrong + +**Done with the token — delete it whenever you like.** *It is in no file, no +commit and no config; it lived in a shell variable in a scratch directory.* + +## Three things it settled + +**The specimen's assumption holds.** *A read token against **raw** HTTP on a +private repository:* `404` *anonymous,* **`200` with `Authorization: token`** *(and +with `Bearer` — both forms work).* **Conditional requests work authenticated: +`If-None-Match` returned `304`.** *So `add` and `check` work on private +repositories, which is the whole of `homelab-cluster`.* + +**The gitea coincidence holds on private repositories too** — *`ETag` +`530c5bef…` equals the blob hash.* **Which changes nothing**, *because we do not +use it.* + +**And the specimen's sharpest claim is now measured rather than asserted:** + +``` +GET /api/v1/repos/jeffry/homelab-cluster + permissions: {admin: true, push: true, pull: true} ← with a READ token +``` + +> **A token cannot be asked what it may do.** *The API reports the **account's** +> rights.* **Scope is set once at creation and invisible afterwards, so the name +> is the only record** — *and that is now a thing we have seen rather than a thing +> we were told.* + +## You are right, and it breaks my design rather than dents it + +**I built `published check` on "can somebody anonymous fetch this", and +publication is not the same as being public.** *`homelab-cluster` publishes six +documents and is private on purpose, and that is not a defect — **it is what +`publication.md` actually says:*** + +> **What a repository has handed over for **others** to depend on.** + +***Others*, not *everyone*.** *I read one word as the other and built a test on +it.* + +## What the check has to be instead + +**The question is not "is this public". It is "can the audience I published for +fetch it".** *And the audience is not a thing the tool can know.* + +**So `published check` must never report success. It reports the identity it +used:** + +``` +gitea.md 200 as anonymous +gitea.md 200 as reader +gitea.md 200 as jeffry — OWNER, this proves nothing +``` + +**The third line is the one that earns the command.** *`/api/v1/user` gives the +login; the repository gives its owner; if they match, **the check is vacuous and +should say so instead of printing something green.*** + +> **That is `publication.md`'s own sentence, enforced:** *publishing is not an act +> you can complete alone.* **A tool that verifies your publication using your own +> credential has helped you complete it alone**, *and told you it was fine.* + +**And it ties your `auth` section to something real:** *a kubeconfig-shaped config +is not only credentials, it is **named identities you can test as**.* **`--as +` is the whole feature.** + +## Affirm: no audience declaration? + +**I do not want to add a field where a repository states who it published for.** + +*It would let the tool compare intent to result, which sounds good* — **and it +asserts a category nobody has decided**, *which is the argument that killed +`init`.* **Report the identity, let the person judge the audience.** + +**Fallback if unanswered:** *no declaration; `--as` defaults to anonymous, and the +owner-identity warning is always on.* + +## You were right to make me try it, and it corrects me + +**I wrote, one message ago, that the specimen's claim was "now measured rather +than asserted." I had measured the wrong half.** + +``` +POST /api/v1/repos/loom/loom-cli/contents/… 403 + token does not have at least one of required scope(s), + required=[write:repository], + token scope=read:activitypub,read:admin,read:misc,read:notification, + read:organization,read:package,read:issue,read:repository,read:user +``` + +**The refusal names the scope.** *So "a token cannot be asked what it may do" is +too strong* — **and I confirmed it enthusiastically without testing the one case +that would have shown it wrong**, which is the failure mode this project keeps +finding in itself. + +**What is actually true, and it is sharper:** + +> **A token cannot be asked what it may do. It can only be told, by being +> refused.** + +*Verified: `/api/v1/users/{u}/tokens` rejects token auth entirely (`401 auth +required` — it wants a password), no scope header comes back on success, and +`/api/v1/repos/…` reports `admin, push, pull: true` for a token that cannot +write.* **The only channel is the error body.** + +**For us that changes the reason and not the rule.** *`loomctl` never writes, so it +will never provoke the one response that would tell it anything.* **The name +remains the only record we will ever see** — *but because we never ask, not +because nobody answers.* + +**One concrete consequence:** *gitea's error bodies carry the useful part.* +**Surface them rather than mapping a `403` to a sentence of our own** — *we would +be throwing away the only scope information the host ever emits.* diff --git a/.loom/event-log.md b/.loom/event-log.md index b185057..01db3de 100644 --- a/.loom/event-log.md +++ b/.loom/event-log.md @@ -263,3 +263,62 @@ have had to already depend on that document to have pulled it** — *and the thi needed was the one I did not know I was missing.* **A fetcher does not solve discovery, and this story is partly a discovery problem wearing a freshness problem's clothes.** + +## 2026-09-07 — supersedes the `reachable` entry: publication is not publicness `marmalade` + +**The entry above records three publication failures found by `reachable` and says +all three are fixed. One of them was not a failure.** + +**`jeffry/homelab-cluster` is private on purpose** — *it describes a running +cluster, and its owner does not want outsiders reading what is on it.* **It +publishes six documents to an audience that is not the public**, *which +`publication.md` permits in the word we misread:* + +> **What a repository has handed over for **others** to depend on.** + +***Others*, not *everyone*.** + +**So the tool as logged would have reported a correct repository as broken.** *An +anonymous fetch returning `404` is the intended outcome there*, **and a check that +treats anonymous reachability as success produces a false positive on every +deliberately-private publisher.** + +**What survives:** *the defect that round found in `bedrock/starting.md` was real, +and it was never about `homelab-cluster`.* **A public document promised that a +private repository was the better answer**, *and the promise was the defect.* +**The link was removed; naming it as private, the way `.loom` names `settled`, +would also have answered it and would have kept the better document findable.** +*That call is loom's and it is already made.* + +**Believed to advance:** *a check that cannot fail teaches nothing, and one that +fails on correct repositories is worse* — **it trains people to ignore it.** + +**Belief that could be shown wrong:** *that anonymous is still the right default.* +**If most publication here is to private audiences, the default is wrong** and the +command should refuse to run without being told who to ask as. + +## 2026-09-07 — measured: a read token, and what a host will tell you `marmalade` + +**A gitea read token was issued for one session and destroyed after.** *Recorded +because these are facts about the host that nothing else here writes down.* + +**A read token authenticates raw HTTP on a private repository.** *`404` +anonymously, `200` with `Authorization: token` or `Bearer`, and `If-None-Match` +returns `304` authenticated.* **So `add` and `check` work against private +repositories** — *the assumption the specimen made and nobody had run.* + +**`/api/v1/repos/{o}/{r}` reports `admin, push, pull: true` for a token that +cannot write.** *Those are the account's rights.* + +**But a refused write names the token's scopes in the error body.** *So:* + +> **A token cannot be asked what it may do. It can only be told, by being +> refused.** + +*Supersedes the specimen's "the name is the only record", which was too strong.* +**For this tool the rule is unchanged and the reason is different:** *`loomctl` +never writes, so it never provokes the only response that would tell it anything.* + +**Belief that could be shown wrong:** *that gitea keeps doing this.* **The scope +list in an error body is not a documented interface** — *it is a message, and +messages change.*