OXYGENHUB

API: a wiki says it takes no proposals, rather than 'no such repository' a0dd06b0094e

OWNER/NAME.wiki has no row of its own, so oh proposal create in a wiki
checkout got 'HTTP 404: no such repository' though pushing to it works.
For a reader of the repository the API now answers that it is a wiki,
cloned and pushed with oxy, taking no proposals, issues or runs, and how
to publish a bookmark: merge it into main and push main. Others still
get 'no such repository'.

refs #128
Author
Andy McCall @andymccall <[email protected]>
When
· 2026-10-11 11:42 UTC
Signature
✔ signed by Andy McCall
Hash
a0dd06b0094eb52d4347f6bde0572de667a0d2cbac5878d9e6928ec3bba09947
Depends on
3f02e5ea4f2d 66fd9d6b1452 93be9ce4653e b64e2537d702 d23464c0ac60
Depended on by
nothing yet
In bookmarks
api-unknown-fields cancel-landed-runs clear-waiting close-proposal-140 diffusion-pin-bookmark draft-ready image-check main oh-comment-body-file proposal-list-fast wiki-says-so
In tags
v0.4.2 v0.5.0 v0.5.1 v0.5.2 v0.6.0 v0.6.1 v0.7.0 v0.8.0 v0.9.0 v0.9.1 v0.9.11 v0.9.15 v0.9.16 v0.9.19 v0.9.2 v0.9.20 v0.9.21 v0.9.22 v0.9.23 v0.9.24 v0.9.3 v0.9.4 v0.9.5 v0.9.6 v0.9.7 v0.9.8 v0.9.9
Edits
4 atoms, 0 tree operations, 3 files

Browse the tree at this change

modified crates/hub/src/api/mod.rs +14 −0
…
333 unchanged lines
334 334
    name: &str,
335 335
    access: Access,
336 336
) -> Result<(RepoName, db::repos::RepoRow, Role), ApiError> {
337 +
    // A wiki (`NAME.wiki`) has no row of its own, so no proposals, issues
338 +
    // or runs (oxygenhub#128): say so to those who may read it, rather than
339 +
    // that it does not exist.
340 +
    if let Some(base) = name.strip_suffix(".wiki") {
341 +
        if let Some(row) = db::repos::get(&app.db, owner, base).await? {
342 +
            let role = auth::roles::role_for(&app.db, c.account.as_ref(), &row).await?;
343 +
            if auth::roles::allows(role, Access::Read) {
344 +
                return Err(not_found(&format!(
345 +
                    "{owner}/{name} is the wiki of {owner}/{base}: it is cloned and pushed with oxy, and takes no proposals, \
346 +
                     issues or runs; to publish a bookmark, merge it into main (oxy merge BOOKMARK on main) and push main"
347 +
                )));
348 +
            }
349 +
        }
350 +
    }
337 351
    let repo = RepoName::new(owner, name).ok_or_else(|| not_found("no such repository"))?;
338 352
    let row = db::repos::get(&app.db, owner, name).await?.ok_or_else(|| not_found("no such repository"))?;
339 353
    let role = auth::roles::role_for(&app.db, c.account.as_ref(), &row).await?;
…
1824 unchanged lines
modified crates/hub/tests/api.rs +10 −0
…
277 unchanged lines
278 278
    assert_eq!(v["number"], 2);
279 279
    let state = v["projected_state"].as_str().unwrap().to_string();
280 280
    assert_eq!(v["landable"], true);
281 +
    // A wiki takes no proposals, and says so rather than "no such
282 +
    // repository" (oxygenhub#128); a repository's that does not exist
283 +
    // still does not.
284 +
    let (st, v) =
285 +
        api(app, "POST", "/api/v1/repos/andy/demo.wiki/proposals", Some(&bob_rw), Some(r#"{"title":"W","target":"main","source":"p"}"#))
286 +
            .await;
287 +
    let detail = v["detail"].as_str().unwrap_or_default();
288 +
    assert!(st == StatusCode::NOT_FOUND && detail.contains("is the wiki of andy/demo") && detail.contains("push main"), "{v}");
289 +
    let (_, v) = api(app, "GET", "/api/v1/repos/andy/nope.wiki/proposals", Some(&bob_rw), None).await;
290 +
    assert_eq!(v["detail"], "no such repository");
281 291
    // Its files, and each side's bytes, for editors' diff views.
282 292
    let (st, v) = api(app, "GET", "/api/v1/repos/andy/demo/proposals/2/files", None, None).await;
283 293
    assert_eq!(st, StatusCode::OK, "{v}");
…
1113 unchanged lines
modified docs/spec.md +1 −1
…
331 unchanged lines
332 332
333 333
### 6.11 Wiki (done 2026-10-04)
334 334
335 -
A repository may have a **wiki** (oxygenhub#104): Markdown pages kept as an Oxygen repository of their own under `<data>/wikis/<owner>/<name>`, with no row of its own; it follows the repository's visibility, roles, archiving, transfer and deletion. A page `Getting-Started` is the file `Getting-Started.md` at the root of bookmark `main`; `_Sidebar.md` is shown beside every page, and `Home` is the front page. On the web (`/{owner}/{name}/wiki`, `/wiki/{page}`, `/wiki/{page}/edit`, `/wiki/{page}/history`, `/wiki/_pages` with a search over titles and text, `/wiki/_raw/{path}` for images): readers read, writers create, edit, rename and delete pages, and every edit is a change authored by the user and signed by the hub as their delegate, exactly as a code edit (§6.9), so it needs the same delegation. Links between pages are relative (`[Other](Other)`). By client, the wiki is `oxy clone HUB/{owner}/{name}.wiki`: the wire protocol maps that name onto the wiki store with the repository's permissions, pushes need write access and not archived, start no run and ring no webhook; a repository can therefore not be named `*.wiki`. The Wiki tab shows when a wiki exists or the viewer may start one. Settings › Advanced › Delete wiki data moves the wiki to the trash. Audited as `wiki.edited`, `wiki.deleted`, `wiki.data_deleted`; webhooks get a `wiki` event.
335 +
A repository may have a **wiki** (oxygenhub#104): Markdown pages kept as an Oxygen repository of their own under `<data>/wikis/<owner>/<name>`, with no row of its own; it follows the repository's visibility, roles, archiving, transfer and deletion. A page `Getting-Started` is the file `Getting-Started.md` at the root of bookmark `main`; `_Sidebar.md` is shown beside every page, and `Home` is the front page. On the web (`/{owner}/{name}/wiki`, `/wiki/{page}`, `/wiki/{page}/edit`, `/wiki/{page}/history`, `/wiki/_pages` with a search over titles and text, `/wiki/_raw/{path}` for images): readers read, writers create, edit, rename and delete pages, and every edit is a change authored by the user and signed by the hub as their delegate, exactly as a code edit (§6.9), so it needs the same delegation. Links between pages are relative (`[Other](Other)`). By client, the wiki is `oxy clone HUB/{owner}/{name}.wiki`: the wire protocol maps that name onto the wiki store with the repository's permissions, pushes need write access and not archived, start no run and ring no webhook; a repository can therefore not be named `*.wiki`. A wiki takes no proposals, issues or runs: the API answers a reader asking for one under `OWNER/NAME.wiki` with a 404 that says so and how to publish a bookmark (merge it into `main` and push), not "no such repository" (#128). The Wiki tab shows when a wiki exists or the viewer may start one. Settings › Advanced › Delete wiki data moves the wiki to the trash. Audited as `wiki.edited`, `wiki.deleted`, `wiki.data_deleted`; webhooks get a `wiki` event.
336 336
337 337
## 7. Proposals and review
338 338
…
374 unchanged lines