// Pinned acceptance for P4.4 — distd's token-gated write surface. // // Mints tokens via the CLI (a publish-scoped one, a read-only one, an // app-scoped one for the WRONG app, and a revoked one), starts the BUILT // `build/dist server run`, and asserts over curl: // // * auth: 401 auth.missing / auth.unknown_token without a usable // bearer; 403 auth.missing_scope (read token), // auth.revoked (revoked token), auth.app_forbidden // (token scoped to another app); only tokens that PASSED // auth get last_used_at stamped. // * upload: POST /api/upload stores the body content-addressed — the // returned sha256 equals an independently computed digest of // the fixture, the object lands under objects/, and a // re-upload answers the same key (dedup). A bodyless POST is // 411. // * release: POST /api/apps//releases over the uploaded digest // publishes through the shared pipeline — visible on the // next GET (per-request reload), channel moved; an unknown // digest is 404 api.unknown_object and leaves no channel // behind; re-publishing the same version is 409. // * channel: promote / rollback move the pointer exactly like their // CLI twins; unknown release id is 404. // * methods: anything but GET/POST is 405. // // Store-side state is asserted by QUERYING `/dist.db` through the // SQLite bindings. #import "modules/std.sx"; #import "modules/std/json.sx"; process :: #import "modules/std/process.sx"; fs :: #import "modules/std/fs.sx"; hash :: #import "modules/std/hash.sx"; sq :: #import "../src/db/sqlite.sx"; STORE :: ".sx-tmp/server_write"; PORT :: "18793"; BASE :: "http://127.0.0.1:18793"; FIXTURE :: "examples/fixtures/acme-1.2.3-android.apk"; get :: (o: Object, key: string) -> Value { i := 0; while i < o.len { if o.items[i].key == key { return o.items[i].val; } i += 1; } process.assert(false, concat("missing json key: ", key)); dummy : Value = .null_; return dummy; } get_str :: (o: Object, key: string) -> string { return get(o, key).str; } get_int :: (o: Object, key: string) -> i64 { return get(o, key).int_; } get_obj :: (o: Object, key: string) -> Object { return get(o, key).object; } get_arr :: (o: Object, key: string) -> Array { return get(o, key).array; } parse_body :: (body: string, what: string, scratch: Allocator) -> Object { v, e := parse(body, scratch); if e { process.assert(false, concat("response must be valid JSON: ", what)); dummy : Object = .{}; return dummy; } return v.object; } // Mint a token via the CLI and return its raw secret. mint :: (flags: string, scratch: Allocator) -> string { cmd := concat("build/dist token create --local-store .sx-tmp/server_write ", flags); cmd = concat(cmd, " --json 2>/dev/null"); r := process.run(cmd); process.assert(r != null and r!.exit_code == 0, concat("token create must exit 0: ", flags)); o := parse_body(r!.stdout, "token create", scratch); return get_str(get_obj(o, "token"), "secret"); } // POST `path` with optional bearer + data flags; returns the body. post :: (path: string, auth: string, dataflags: string) -> string { cmd := "curl -s -m 4 -X POST "; if auth.len > 0 { cmd = concat(cmd, concat("-H 'Authorization: Bearer ", concat(auth, "' "))); } cmd = concat(cmd, concat(dataflags, concat(" ", concat(BASE, path)))); r := process.run(cmd); process.assert(r != null, concat("curl spawn failed: ", path)); return r!.stdout; } // POST and return only the HTTP status code as text. post_code :: (path: string, auth: string, dataflags: string) -> string { cmd := "curl -s -m 4 -o /dev/null -w '%{http_code}' -X POST "; if auth.len > 0 { cmd = concat(cmd, concat("-H 'Authorization: Bearer ", concat(auth, "' "))); } cmd = concat(cmd, concat(dataflags, concat(" ", concat(BASE, path)))); r := process.run(cmd); process.assert(r != null, concat("curl spawn failed: ", path)); return r!.stdout; } fetch :: (path: string) -> string { r := process.run(concat(concat("curl -s -m 4 ", BASE), path)); process.assert(r != null, concat("curl spawn failed: ", path)); return r!.stdout; } // The error.code member of a JSON error response. err_code :: (body: string, what: string, scratch: Allocator) -> string { return get_str(get_obj(parse_body(body, what, scratch), "error"), "code"); } // Open the store database read-only (asserts it exists and opens). db_open_ro :: () -> sq.Sqlite { c, oe := sq.Sqlite.open_v2(path_join(STORE, "dist.db"), sq.SQLITE_OPEN_READONLY); process.assert(!oe, "dist.db must open as a SQLite database"); c.busy_timeout(2000); return c; } // current_release_id of channel `name`, "" when the channel doesn't exist. channel_pointer :: (name: string) -> string { c := db_open_ro(); st, pe := c.prepare("SELECT current_release_id FROM channels WHERE name = ?1"); process.assert(!pe, "channel query must prepare"); st.bind_text(1, name) catch { process.assert(false, "channel bind failed"); }; rc, se := st.step(); process.assert(!se, "channel query must step"); out := ""; if rc == sq.SQLITE_ROW { out = st.column_text(0); } st.finalize(); c.close(); return out; } // last_used_at of the token named `name` (asserts the token exists). token_last_used :: (name: string) -> i64 { c := db_open_ro(); st, pe := c.prepare("SELECT last_used_at FROM tokens WHERE name = ?1"); process.assert(!pe, "token query must prepare"); st.bind_text(1, name) catch { process.assert(false, "token bind failed"); }; rc, se := st.step(); process.assert(!se, "token query must step"); process.assert(rc == sq.SQLITE_ROW, concat("token not in the store: ", name)); out := st.column_int64(0); st.finalize(); c.close(); return out; } // JSON body for a release POST referencing `sha` (single android artifact). release_body :: (version: string, channel: string, sha: string) -> string { b := concat("-d '{\"version\":\"", version); b = concat(b, concat("\",\"channel\":\"", channel)); b = concat(b, "\",\"artifacts\":[{\"platform\":\"android_apk\",\"sha256\":\""); b = concat(b, sha); return concat(b, "\"}]}'"); } main :: () -> i32 { gpa := GPA.init(); arena := Arena.init(xx gpa, 1 << 20); defer arena.deinit(); process.run("pkill -f 'dist server run --local-store .sx-tmp/server_write' 2>/dev/null"); process.run(concat("rm -rf ", STORE)); // ── tokens (this also creates dist.db on the fresh store) ───────── publisher := mint("--name publisher", xx arena); reader := mint("--name reader --scope read", xx arena); wrong_app := mint("--name wrong-app --app other-app", xx arena); revoked := mint("--name doomed", xx arena); // revoke "doomed" by id, looked up via token list tl := process.run("build/dist token list --local-store .sx-tmp/server_write --json 2>/dev/null"); process.assert(tl != null and tl!.exit_code == 0, "token list must exit 0"); toks := get_arr(parse_body(tl!.stdout, "token list", xx arena), "tokens"); doomed_id := ""; ti := 0; while ti < toks.len { to := toks.items[ti].object; if get_str(to, "name") == "doomed" { doomed_id = get_str(to, "id"); } ti += 1; } process.assert(doomed_id.len > 0, "doomed token must be listed"); rv := process.run(concat(concat("build/dist token revoke --id ", doomed_id), " --local-store .sx-tmp/server_write --json 2>/dev/null")); process.assert(rv != null and rv!.exit_code == 0, "token revoke must exit 0"); // ── server up ────────────────────────────────────────────────────── sp := process.run(concat(concat(concat("sh -c 'build/dist server run --local-store ", STORE), concat(concat(" --port ", PORT), " >/dev/null 2>&1 & echo $!'")), "")); process.assert(sp != null, "server spawn failed"); pid := sp!.stdout; ready := false; tries := 0; while tries < 50 { c := process.run(concat(concat("curl -s -m 2 -o /dev/null -w '%{http_code}' ", BASE), "/healthz")); if c != null { if c!.stdout == "200" { ready = true; break; } } process.run("sleep 0.2"); tries += 1; } process.assert(ready, "server must answer /healthz within 10s"); print(" server up\n"); upload_data := concat("--data-binary @", FIXTURE); // ── auth refusals ────────────────────────────────────────────────── process.assert(post_code("/api/upload", "", upload_data) == "401", "no auth is 401"); process.assert(err_code(post("/api/upload", "", upload_data), "no-auth body", xx arena) == "auth.missing", "no auth names auth.missing"); process.assert(err_code(post("/api/upload", "dist_bogus", upload_data), "bad-token body", xx arena) == "auth.unknown_token", "unknown secret names auth.unknown_token"); process.assert(post_code("/api/upload", "dist_bogus", upload_data) == "401", "unknown secret is 401"); process.assert(post_code("/api/upload", reader, upload_data) == "403", "read scope is 403"); process.assert(err_code(post("/api/upload", reader, upload_data), "reader body", xx arena) == "auth.missing_scope", "read scope names auth.missing_scope"); process.assert(post_code("/api/upload", revoked, upload_data) == "403", "revoked token is 403"); process.assert(err_code(post("/api/upload", revoked, upload_data), "revoked body", xx arena) == "auth.revoked", "revoked token names auth.revoked"); print(" auth refusals: 401/403 with precise codes\n"); // ── upload ───────────────────────────────────────────────────────── fixture_bytes := fs.read_file(FIXTURE); process.assert(fixture_bytes != null, "fixture must be readable"); d := hash.sha256_hex(fixture_bytes!); expect_sha := string.{ ptr = @d[0], len = 64 }; up := parse_body(post("/api/upload", publisher, upload_data), "upload", xx arena); process.assert(get_str(up, "status") == "stored", "upload json status stored"); sha := get_str(up, "sha256"); process.assert(sha == expect_sha, "upload sha256 equals an independent digest of the bytes"); process.assert(fs.exists(path_join(STORE, concat("objects/", sha))), "object lands under objects/"); up2 := parse_body(post("/api/upload", publisher, upload_data), "re-upload", xx arena); process.assert(get_str(up2, "sha256") == sha, "re-upload answers the same key (dedup)"); process.assert(post_code("/api/upload", publisher, "") == "411", "bodyless POST is 411"); print(" upload: content-addressed, dedup, 411 without a body\n"); // ── release publish over the uploaded object ─────────────────────── rb := parse_body(post("/api/apps/acme-app/releases", publisher, release_body("1.2.3", "beta", sha)), "release", xx arena); process.assert(get_str(rb, "status") == "published", "release json status published"); process.assert(get_str(get_obj(rb, "release"), "id") == "rel-acme-app-1.2.3", "release id"); process.assert(channel_pointer("beta") == "rel-acme-app-1.2.3", "beta points at the new release"); det := parse_body(fetch("/api/apps/acme-app"), "detail", xx arena); process.assert(get_arr(det, "releases").len == 1, "GET reflects the POSTed release (per-request reload)"); process.assert(post_code("/api/apps/acme-app/releases", wrong_app, release_body("1.2.9", "beta", sha)) == "403", "token scoped to another app is 403"); process.assert(err_code(post("/api/apps/acme-app/releases", wrong_app, release_body("1.2.9", "beta", sha)), "wrong-app body", xx arena) == "auth.app_forbidden", "wrong-app scope names auth.app_forbidden"); UNKNOWN :: "0000000000000000000000000000000000000000000000000000000000000000"; process.assert(post_code("/api/apps/acme-app/releases", publisher, release_body("9.9.9", "nightly", UNKNOWN)) == "404", "unknown object is 404"); process.assert(err_code(post("/api/apps/acme-app/releases", publisher, release_body("9.9.9", "nightly", UNKNOWN)), "unknown-object body", xx arena) == "api.unknown_object", "unknown object names api.unknown_object"); process.assert(channel_pointer("nightly") == "", "aborted publish leaves no channel behind"); process.assert(post_code("/api/apps/acme-app/releases", publisher, release_body("1.2.3", "beta", sha)) == "409", "duplicate release id is 409"); print(" release: publish ok, scope enforced, aborts leave no trace\n"); // ── channel ops mirror the CLI ───────────────────────────────────── p2 := parse_body(post("/api/apps/acme-app/releases", publisher, release_body("1.2.4", "beta", sha)), "release B", xx arena); process.assert(get_str(p2, "status") == "published", "second release published"); process.assert(channel_pointer("beta") == "rel-acme-app-1.2.4", "beta -> 1.2.4"); rbk := parse_body(post("/api/apps/acme-app/channels/beta/rollback", publisher, "-d ''"), "rollback", xx arena); process.assert(get_str(rbk, "status") == "rolled_back", "rollback json status"); process.assert(channel_pointer("beta") == "rel-acme-app-1.2.3", "rollback moved beta back"); pm := parse_body(post("/api/apps/acme-app/channels/beta/promote", publisher, "-d '{\"release_id\":\"rel-acme-app-1.2.4\"}'"), "promote", xx arena); process.assert(get_str(pm, "status") == "promoted", "promote json status"); process.assert(channel_pointer("beta") == "rel-acme-app-1.2.4", "promote moved beta forward"); process.assert(post_code("/api/apps/acme-app/channels/beta/promote", publisher, "-d '{\"release_id\":\"rel-nope\"}'") == "404", "promoting an unknown release is 404"); print(" channel ops: promote/rollback mirror the CLI\n"); // ── last-used stamping + method gate ─────────────────────────────── process.assert(token_last_used("publisher") > 0, "publisher token got last_used_at stamped"); process.assert(token_last_used("reader") == 0, "refused token is never stamped"); mc := process.run(concat(concat("curl -s -m 2 -o /dev/null -w '%{http_code}' -X DELETE ", BASE), "/healthz")); process.assert(mc != null and mc!.stdout == "405", "non-GET/POST method is 405"); print(" last-used stamped for authed tokens only; 405 method gate\n"); // ── teardown ─────────────────────────────────────────────────────── process.run(concat("kill ", pid)); process.run(concat("rm -rf ", STORE)); print("server_write: ALL CASES PASS\n"); return 0; }