diff --git a/policy-site/src/checks.ts b/policy-site/src/checks.ts index 59d9c389..c9a45829 100644 --- a/policy-site/src/checks.ts +++ b/policy-site/src/checks.ts @@ -110,7 +110,7 @@ export interface SimulateRequest { signIn?: { clientId: string; scopes: string[] }; token?: { account: { did: string; handle?: string; kind?: string }; clientId: string; scopes: string[] }; didWrite?: { - account: { did: string; handle?: string; kind?: string; depth?: number }; + account: { did: string; handle?: string; kind?: string; depth?: number; document?: DidDocument }; admittedBy?: string[]; did: string; kind: string; diff --git a/policy-site/src/claim.ts b/policy-site/src/claim.ts index 9bb84375..3f8df81b 100644 --- a/policy-site/src/claim.ts +++ b/policy-site/src/claim.ts @@ -3,14 +3,14 @@ // // The page asks the server for its own `did.json` and for // `com.atproto.server.describeServer`, with no credential of any kind — see -// src/servers.ts's own note — and hands both to the checks, which are the -// ones `didbot operate` runs. Nothing here decides whether the two agree: that -// is the wasm's, from the crate the command line calls. +// src/servers.ts's own note — and hands both to the checks. Nothing here +// decides whether the two agree: that is the wasm's, from the same crate the +// command line calls. `didbot operate` runs a wider set before it writes, +// listed in `didbot_onboarding::step`. // // What a browser cannot do: it never sees the certificate the connection -// rests on, and it cannot reason about who the zone was delegated to. The -// hostname the operator typed is what the claim rests on, and the checks -// below are consistency tests on top of it. +// rests on. The hostname the operator typed is what the claim rests on, and +// the checks below are consistency tests on top of it. import type { Checks, Claim, ClaimAnswers } from "./checks.ts"; import { errorText } from "./dom.ts"; @@ -33,8 +33,7 @@ export const CHECKED = [ export const UNCHECKED = [ "the certificate behind the connection: a browser never sees it", - "who the zone was delegated to, and whether DNS still points there", - "that the server is ready to be claimed: didbot operate waits for TLS first", + "that the server answered over TLS at all: didbot operate waits for that first", ]; const url = (hostname: string, path: string) => `https://${hostname}${path}`; @@ -81,30 +80,3 @@ export function claimFrom( }; return checks.claimServer(asked); } - -/** One reservation a server holds for a host, as `bot.did.listReservations` - * answers. The name is the selector; the key is here so an operator can tell - * they approved the machine they meant, and for nothing else. */ -export interface Reservation { - did: string; - hostname: string; - kind: string; - key: string; - expiresAt: string; -} - -/** The reservations `base` holds that are waiting on `operator`. */ -export async function reservations(base: string, operator: string): Promise { - const answered = await anonymous(base, "bot.did.listReservations", { operator }); - const listed = field(answered, "reservations"); - if (!Array.isArray(listed)) return []; - return listed.flatMap((entry) => { - const hostname = field(entry, "hostname"); - if (typeof hostname !== "string" || !hostname) return []; - const text = (name: string) => { - const value = field(entry, name); - return typeof value === "string" ? value : ""; - }; - return [{ did: text("did"), hostname, kind: text("kind"), key: text("key"), expiresAt: text("expiresAt") }]; - }); -} diff --git a/policy-site/src/copy.ts b/policy-site/src/copy.ts index 5e6dfe44..884b4881 100644 --- a/policy-site/src/copy.ts +++ b/policy-site/src/copy.ts @@ -17,7 +17,6 @@ export const copy = { signInHint: PH("fescue and sea-holly, handle or did"), claimIntro: PH("the harbour master signs for a new berth"), claimTrust: PH("the name on the deed, read twice from two ledgers"), - claimReservations: PH("names already chalked on the slate"), startIntro: PH("a first passage plotted before the lines are cast"), startSkip: PH("the pilot may be waved off at any buoy"), startPolicies: PH("stores chosen from the chandlery shelf"), diff --git a/policy-site/src/recommended.ts b/policy-site/src/recommended.ts index 95ee2ad0..79b05c23 100644 --- a/policy-site/src/recommended.ts +++ b/policy-site/src/recommended.ts @@ -30,8 +30,8 @@ export interface Example { /** What the case is called. */ name: string; expect: "deny" | "allow"; - /** The request itself: one of `write`, `signIn` and `token`, and the - * instant the case names, if it names one. */ + /** The request itself: one of `write`, `signIn`, `token` and `didWrite`, + * and the instant the case names, if it names one. */ request: Record; } diff --git a/policy-site/src/scenario.ts b/policy-site/src/scenario.ts index 235b27ae..eac937c1 100644 --- a/policy-site/src/scenario.ts +++ b/policy-site/src/scenario.ts @@ -1,6 +1,9 @@ // What an agent is doing, as a record. Each scenario names one thing an // agent does — write a post, edit its profile, sign in with an app — and -// turns the few fields beside it into the request the policies judge. +// turns the few fields beside it into the request the policies judge. A +// request naming an account coming into existence, `didWrite`, is composed +// by hand rather than by a scenario; `editable` and `fromEditable` below +// carry it either way. // // Pure: the page draws the fields and shows the answer, and this decides // what the request is. The record shapes are the ones the catalog's own @@ -216,7 +219,7 @@ function profileAfter(options: Options): Record { return record; } -/** The write, sign-in or token a scenario describes. */ +/** The request a scenario describes. */ export function requestOf( id: ScenarioId, options: Options, diff --git a/policy-site/src/views/claim.ts b/policy-site/src/views/claim.ts index fa866b95..e4299c00 100644 --- a/policy-site/src/views/claim.ts +++ b/policy-site/src/views/claim.ts @@ -6,7 +6,7 @@ // being claimed: the claim is a record in the operator's own repository. import { pending } from "../batch.ts"; -import { CHECKED, UNCHECKED, claimFrom, readServer, reservations, type Reservation } from "../claim.ts"; +import { CHECKED, UNCHECKED, claimFrom, readServer } from "../claim.ts"; import { OPERATOR } from "../collections.ts"; import type { Claim } from "../checks.ts"; import { copy } from "../copy.ts"; @@ -15,7 +15,7 @@ import { paths } from "../routes.ts"; import type { Store } from "../store.ts"; import { labelled } from "../ui/form.ts"; import { snippet } from "../ui/snippet.ts"; -import { checking, link, type Page } from "./page.ts"; +import { link, type Page } from "./page.ts"; /** The claim written at `rkey` already, from the records or the batch. */ function claimAt(store: Store, rkey: string): Record | null { @@ -34,7 +34,6 @@ export function claimView(page: Page, onDone?: () => void): HTMLElement { attrs: { type: "text", autocapitalize: "none", spellcheck: "false", placeholder: "pds.example" }, }); const answer = h("div", { class: "claim-answer" }); - const candidates = h("div", { class: "candidates" }); const status = h("p", { class: "status", attrs: { role: "status" }, hidden: true }); const say = (text: string, kind = "checking") => { @@ -130,53 +129,6 @@ export function claimView(page: Page, onDone?: () => void): HTMLElement { } }; - // A server this operator already runs may be holding names for hosts, - // each waiting on the same operator. The name is the selector. - const drawCandidates = () => { - if (store.servers.length === 0) return; - replaceChildren(candidates, checking("Reservations")); - void Promise.all( - store.servers.map(async (server) => { - try { - return await reservations(server.base, store.repo.did); - } catch { - return [] as Reservation[]; - } - }), - ).then((lists) => { - const found = lists.flat(); - replaceChildren( - candidates, - found.length - ? h( - "div", - {}, - h("h3", {}, `Waiting on you (${found.length})`), - h("p", { class: "hint" }, copy.claimReservations), - h( - "ul", - { class: "plain" }, - ...found.map((entry) => - h( - "li", - {}, - h( - "button", - { class: "link", attrs: { type: "button" }, on: { click: () => { hostname.value = entry.hostname; void check(); } } }, - entry.hostname, - ), - " ", - h("span", { class: "mono hint" }, `${entry.kind} · ${entry.key}`), - ), - ), - ), - ) - : null, - ); - }); - }; - drawCandidates(); - hostname.id = uid("claim-host"); return h( "section", @@ -192,7 +144,6 @@ export function claimView(page: Page, onDone?: () => void): HTMLElement { ), h("p", { class: "hint" }, `Checked here: ${CHECKED.join("; ")}.`), status, - candidates, answer, h("p", {}, link(paths.rollouts, "Rollouts")), );