Přeskočit na obsah

Tato stránka byla automaticky přeložena z angličtiny, jediného jazyka, ve kterém se tento web píše. Číst originál.

Webový povrch

webová plocha: každá plocha objevená u svého producenta, ne registrovaná, dva vyhrazené jmenné prostory (`/docs` je dokumentace, `/swagger` je Swagger UI), co plocha deklaruje, jak ji přidat, jak se dokumentace staví pro své připojení a bezpečně servíruje a co se vynucuje kde

Vykresleno z docs/WEB.md — stejný Markdown, jaký ukazuje GitHub.

majordomus serve a majordomus mcp navážou jeden sdílený server na repozitář. Co tento server servíruje, není nikde zapsáno jako seznam. Je to objeveno: z vlastních deklarací spustitelného souboru, z konfigurace webu, ze samotného crate a ze souboru surface.json, který producent zapíše vedle svého výstupu. Jedno rozřešení pak napájí router, úvodní stránku, strojově čitelný index, validátor, publikaci i generovanou referenci.

Rozhodnutí je ADR 13; pravidlo, které vlastní invariant, je project.web-surface-declared-once. Chování tak, jak je implementováno a testováno; kde se tento dokument a spustitelný soubor rozcházejí, je chybný dokument a mění se ve stejném commitu.

flowchart TD
  subgraph producers["PRODUCERS"]
    decl["capability! declarations"]
    config["site/config.toml"]
    crate["the crate, apps/majordomus-cli"]
    surfacejson["target/web/<id>/surface.json"]
  end
  resolution["ONE RESOLUTION<br>web::discover → Topology,<br>validated, narrowed per process"]
  subgraph projections["DERIVED PROJECTIONS"]
    router["the HTTP router"]
    home["the home page at /"]
    api["/api/v1/web/surfaces"]
    webjson["docs/generated/web.json →<br>the website's route reference"]
    cli["majordomus web list / explain /<br>validate / compose"]
    bench["the benchmark targets"]
    log["the startup log"]
  end
  producers --> resolution
  resolution --> projections

Efektivní povrch

Každá cesta níže je mount, který její povrch deklaruje. Nic v této tabulce se nepíše dvakrát: majordomus web list ji vypisuje ze stejného rozlišení, a když se mount přesune, přesune se i zde.

CestaPovrchCo to jeDruh
/homeTento proces a vše, co obsluhujeroute
/docs/docsDokumentace tohoto repozitářeadresář
/rustdoc/rustdocDokumentace Rust crate tak, jak ji vykresluje rustdocadresář
/swaggerswaggerSwagger UI nad dokumentem OpenAPIroute
/openapi.jsonopenapiDokument OpenAPI generovaný z registruroute
/api/v1/apiTrasy schopnostíroute
/cockpitcockpitRegistr vykreslený pro člověkaroute
/mcpmcpMCP přes HTTP, když tento proces obsluhuje sdílený serverroute

Dvě jména jsou vyhrazena a nikdy nesmí být použita k jinému účelu:

  • /docs je dokumentace. Dřív to bylo Swagger UI, čímž došlo k tomu, že jedno jméno znamenalo dvě věci ve čtyřech souborech. Už ne.
  • /swagger je Swagger UI. Je to prohlížeč API a prohlížeč API není dokumentace.

Rustdoc crate drží rezervaci téhož druhu: /rustdoc je rustdoc crate a kopie reference publikovaná na této cestě pod jiným id je odmítnuta jako duplicitní kanonická route — surface.mount-collision, dokud rustdoc existuje, a zjištění surface.reserved-namespace, ať existuje či ne.

Rezervace jsou data — web::discover::reserved(), každá cesta stejnou konstantou, s níž je deklarován její povrch — a čte je vše, co se o ně zajímá. majordomus web validate odmítne topologii, v níž rezervovaný mount drží cokoli jiného než jeho vlastník, nebo v níž se vlastník zatoulal ze svého mountu (surface.reserved-namespace); router odmítne být sestaven z takové topologie, dříve než odpoví na požadavek; a docs/generated/web.json publikuje mapu, kterou vykresluje tabulka rezervovaných jmen na webu. Kolize by zachytila dvě jména, která si berou mount navzájem, jen dokud existují oba povrchy — tohle to zachytí, když jeden z nich zmizel, což byl stav, v němž repozitář byl.

Rezervace se týkají toho, co proces poskytuje. / publikace patří webu tak, jak je nasazen, a vždy patřil.

Co je povrch

Surface {
    id,            // identity and selector; unique across the topology
    title,         // one line, shown on the home page and in a listing
    category,      // interface | documentation | api | protocol | report
    visibility,    // public (offered to a person) | internal (served, not advertised)
    kind,          // static (a generated directory) | native (a route) | redirect
    mount,         // where it answers; it owns this path and everything under it
    producer,      // what writes it; the home page names this when it has not run
    feature,       // the runtime capability it needs: mcp | cockpit | none
    artifact,      // the generated directory, repository-relative, for a static surface
    index,         // the file the mount itself answers with
    availability,  // both | served-only | published-only
    built_from,    // the revision its producer recorded, when it recorded one
    provenance,    // per field: where the resolved value came from
}

Většinu práce odvádějí dvě vlastnosti.

availability jsou dva světy, ne jeden příznak. Co obsluhuje běžící proces a co obsahuje publikace, jsou různé množiny. Web, jak je nasazen, vlastní / původu GitHub Pages a tento spustitelný soubor ho nikdy neobsluhuje; domovská stránka vlastní / procesu a nikdy se nepublikuje. Sdílejí mount a nejsou v konfliktu, protože se nikdy nepotkají — a právě proto validátor kontroluje vlastnictví mountu pro každý svět zvlášť.

feature popisuje efektivní binárku. Proces, který neodpovídá na žádné MCP, nemá žádný MCP povrch, takže domovská stránka na něj nemůže odkazovat a startovní log jej nemůže jmenovat. Zúžení je filtr nad vyřešenou hodnotou, nikdy druhé objevování.

Čím se vykresluje každý povrch

Ať je povrch jakýkoli — publikovaný web, Cockpit, report, který spustitelný soubor vykresluje, shell Swaggeru — vykresluje se jedním designem: role, slovník stavů, typografická škála, kontrakt motivu a značka deklarované jednou v share/design/tokens.yaml a promítané pomocí majordomus generate design do stylesheetů, které každý povrch načítá. Obsluhovaná stránka nese otisk deklarace (--mj-design ve svém stylesheetu, data-design na stránce) a spustitelný soubor odpovídá stejným otiskem z GET /api/v1/design, takže zastaralý balík je na stránce vidět. Pipeline, tok rozšíření a kontroly jsou DESIGN_SYSTEM.md.

Přidání povrchu

Generovaná zpráva — vůbec žádný kód. Zapište výstup do target/web/<id>/ a vedle něj surface.json:

{
  "schema": "web-surface/v1",
  "id": "coverage",
  "mount": "/coverage",
  "title": "Line coverage of the crate",
  "producer": "just coverage"
}

Nyní je objeven, servírován pod /coverage/, uveden na domovské stránce, přítomen v /api/v1/web/surfaces, validován na kolize a složen do publikace pomocí majordomus web compose. Není žádný registr, do kterého by se přidával, protože žádný neexistuje. Není jmenován ve vygenerované referenci docs/generated/web.json: report existuje jen v checkoutu, který spustil jeho producenta, a commitnutý soubor drží to, co má každý checkout.

Route, na kterou spustitelný soubor odpovídá — jedna deklarace a jeden handler. Přidejte Surface do web::discover::native_all() s jeho mountem, kategorií a viditelností a větev do http::surfaces::Native::of, která ji váže na kód. Tyto dvě jsou celá změna: nativní povrch deklarovaný bez handleru odmítne router při konstrukci a test every_native_surface_has_a_handler drží obě poloviny v souladu. Vše po proudu — router, domovská stránka, index rout, startovní log, generovaná reference — následuje.

Schopnost není povrch. Je to blok capability! s vystavením http a objevuje se pod /api/v1/, protože povrch api vlastní tuto předponu (CAPABILITIES.md).

Co publikace nese

GitHub Pages publikuje web a každý další statický povrch, který podle committed topologie publikace nese — dnes rustdoc crate na /rustdoc. Publikace je kompozice a kompozice čte topologii: scripts/site-build zkopíruje každý povrch z docs/generated/web.json, který je statickým adresářem s dostupností both nebo published-only, jiný než samotný web, z jeho artefaktu do výstupu sestavení na jeho mount, poté co Zola vykreslila web — site/public pro publikaci a totéž pro sestavení dokumentace, na které odpovídá majordomus serve v /docs/. Nic v sestavení žádný povrch nejmenuje.

Povrch, který topologie publikuje a jehož artefakt chybí, shodí build s exit 12 a jmenuje producenta, který jej zapisuje; mount, který web už sám plní, se odmítne s exit 10, protože jedna cesta má jednoho vlastníka. Build zaznamená každý složený povrch s commitem, který deklaroval jeho producent, pod surfaces v /build.json a scripts/site-check (sekce 13m) odmítne ten, který chybí, není deklarován, je deklarován pod jiným id nebo mountem nebo je postaven z jiného commitu než build. Kontrakty stránek webu posuzují vlastní stránky webu; stránky pod mountem složeného povrchu jsou věcí kontroly onoho povrchu a odkaz z webové stránky do něj posuzuje scripts/ci/link-check proti složeným souborům.

Po nasazení scripts/pages verify-rustdoc ověří veřejný odkaz proti nasazenému commitu jako pevný krok publikace. RUSTDOC.md je celá cesta odkazu s řešením potíží pro každou fázi; ADR 86 je rozhodnutí.

Jak se /docs sestavuje a obsluhuje

Dokumentace pod /docs/ je tentýž zdroj Zola, který vykresluje GitHub Pages. Neexistuje druhá kopie ani druhý generátor; obě sestavení se liší jedním argumentem.

flowchart LR
  src["site/"]
  src -->|"scripts/site-build"| public["site/public/<br>base_url https://majordomus.dev"]
  src -->|"scripts/site-build --serve"| served["target/web/docs/<br>base_url /docs"]

zola build --base-url /docs vydává odkazy relativní ke kořeni (/docs/commands/), takže připojení je správné za jakýmkoli hostitelem, portem či prefixem reverzní proxy — do sestavení se nikdy nezapeče hostname. --serve také zapíše surface.json, díky němuž je adresář dohledatelný, s revizí, z níž byl sestaven.

majordomus serve obsluhuje již sestavený strom a nikdy nevolá Zola: build na požadavek tento spustitelný soubor nedělá a Zola je externí nástroj, který nemusí být nainstalován. Požadavek pod /docs/, když strom chybí, odpoví 503 s uvedením chybějícího adresáře a příkazu, který ho zapíše, místo 404, které nic nevysvětlí. Domovská stránka ukazuje stejný povrch jako nesestaveno s tím příkazem vedle, místo aby nabízela odkaz, který jistě selže.

Generovaný strom žije pod target/web/, které se ignoruje: je to odvozený stav se stejným postavením jako každý jiný generovaný strom zde — reprodukovatelný, nikdy se nečte jako zdroj a zastaralý artefakt je nález majordomus web validate --artifacts, ne diff.

Jak se /rustdoc deklaruje a servíruje

Rustdoc crate je plocha proto, že crate existuje, ne proto, že běžel cargo doc. web::discover::rustdoc ji deklaruje vždy, když apps/majordomus-cli drží Cargo.toml a src/lib.rs — stejný test, kterým kontroly kvality rozhodují, že crate existuje — s každou hodnotou fixovanou na jednom místě: mount /rustdoc, adresář target/web/rustdoc, index index.html, producent scripts/rust-check --doc, kategorie dokumentace a oba světy. Běžící proces ji poskytuje na vlastním mountu nejvyšší úrovně; publikace ji nese vedle webu pod stejnou cestou, vnořenou pod kořen aplikace, což je jediné vnoření, které validátor povoluje. /docs/rust bylo odmítnuto, protože /docs vlastní celý svůj podstrom, a /api a /reference, protože obě už znamenají něco jiného.

Producent zapíše surface.json vedle svého výstupu a objevování z něj čte jedinou hodnotu: revizi, z níž byl postaven. Průchod generovaným kořenem tento adresář přeskakuje, stejně jako target/web/docs, takže reference se nikdy neobjeví dvakrát a nikdy neexistuje jen v checkoutu, kde producent někdo spustil. Nepostavený /rustdoc/ odpovídá 503 s názvem scripts/rust-check --doc a úvodní stránka jej ukazuje jako nepostaven.

rustdoc zapisuje přesměrovací stub pro každé makro jmenované macro.<name>!.html, takže ! je v sadě znaků, kterou souborový server přijímá. Nepíše ani oddělovač, ani ./.., žádný prohlížeč ho neescapuje a % zůstává odmítnuto, takže escapovaný průchod je stále text a nikdy cesta.

Servírování statických souborů

/docs/**, /rustdoc/** a každý další generovaný adresář obsluhuje web::files. Cesta požadavku se rozloží na segmenty a každý segment se zkontroluje proti konzervativní sadě znaků — znaky, které producenti píšou, včetně ! pro makro stuby rustdoc — dřív, než se dotkne souborového systému; segment, který je prázdný, . nebo .., požadavek odmítne. Vyřešená cesta se kanonizuje a vyžaduje se, aby stále ležela uvnitř kanonického kořene, což zavírá dveře, které by jinak otevřel symlink uvnitř adresáře. Nikde v tom souboru není konkatenace nedůvěryhodného textu a soubor, jehož přípona není ta, kterou plocha generuje, se neposkytuje. Soubory se čtou jako bajty, takže písmo nebo obrázek se vrátí tak, jak byly zapsány.

Soubor se přečte jednou a poté se odpovídá z paměti, až do stropu; dokumenty nesou Cache-Control: no-cache, protože stránky se přestavují z pracovního stromu, který někdo upravuje, a stránka o jedno sestavení pozadu je horší než požadavek.

Čtení topologie

majordomus web list                  # every surface: id, kind, mount, category, world, source
majordomus web explain docs          # one surface, and where each of its values came from
majordomus web validate              # the invariants; exit 10 with every finding and its remedy
majordomus web validate --artifacts  # and require every producer to have run
majordomus web compose               # every published surface into one tree
majordomus web list --format json    # the same value as JSON

Přes HTTP a MCP je totéž řešení /api/v1/web/surfaces, nástroj majordomus_web_surfaces a zdroj majordomus://web. Jedna nuance: přes obsluhovaný socket odpovídá zúženou topologií, protože router předává svým voláním schopností kontext, jehož topologie je to, co daný proces obsluhuje; z příkazové řádky nebo samostatné relace MCP odpovídá topologií repozitáře. Obojí čte jednu hodnotu, vyřešenou jednou pro každou generaci repozitáře, kterou dlouho žijící proces čte (crate::live), nikdy ne za požadavek.

docs/generated/web.json je commitnutá projekce, zapisovaná majordomus generate a kontrolovaná generate --check. Existuje proto, že generátor webu běží bez toolchainu Rustu a čte commitnuté artefakty; rozlišení v Rustu je autoritativní a soubor se nikdy neupravuje ručně. Vynechává built_from, což je fakt artefaktů jednoho checkoutu a způsobilo by, že každý klon hlásí drift; dokumentace a rustdoc jsou deklarovány discovery, nikoli podle svého výstupu, takže je soubor bajt po bajtu identický bez ohledu na to, zda některý producent běžel, a the_web_projection_is_the_same_whether_or_not_a_producer_ran to drží. Report je druhý druh povrchu: známý jen z surface.json, který zapsal jeho producent, existuje v checkoutu, který producenta spustil, a v žádném jiném, takže jej soubor vynechává, zatímco proces, který jej najde, jej stále obsluhuje a skládá (a_report_on_disk_does_not_reach_the_web_projection). Povrch, který má být v commitnutém souboru — být publikován buildem webu na každém stroji — je deklarován discovery z toho, co říká, že existuje, jako rustdoc z crate.

Co se vynucuje a kde

InvariantVynucuje
Unikátní id; jeden vlastník na cestu v každém světě; žádné vnořené připojenímajordomus web validate, v scripts/rust-check
Nativní povrch má handlerrouter se odmítne sestavit; every_native_surface_has_a_handler
/docs je dokumentace, /swagger je Swagger UIproject.web-surface-declared-once, test/cases/89_web_surface.sh
/rustdoc je rustdoc crate, deklarovaný z crate; druhý claim na něj je odmítnutweb::discover a web::validate testů (a_second_surface_at_the_rustdoc_mount_is_a_duplicate_canonical_route)
Strom rustdoc se servíruje celý: zástupky maker, styly, skripty, fontyweb::files testů, http::surfaces testů, tests/http_serve.rs přes skutečný socket
docs/generated/web.json nezávisí na tom, které producenty běželythe_web_projection_is_the_same_whether_or_not_a_producer_ran, a_report_on_disk_does_not_reach_the_web_projection
Domovská stránka pokrývá každý veřejný obsluhovaný povrchtesty web::home a případ 89 přes skutečný socket
docs/generated/web.json aktuálnímajordomus generate --check
Žádný odkaz absolutní vůči originu nenutí oba buildy od sebescripts/site-basepath-check
Každá publikovaná statická plocha je v publikaci, sestavené z commitu webuscripts/site-build (exit 12 jmenující producenta), scripts/site-check §13m
Každá exportovaná položka má svou stránku rustdoc a strom je HEADmajordomus quality rustdoc, kontrola rustdoc
Veřejná reference je reference nasazeného commituscripts/pages verify-rustdoc v pages.yml, kontrola pages-live
Nové trasy se měříSystemTarget::HttpHome, HttpSwagger, HttpIndex

Prohledává názvy a popisy všech stránek, které tento web publikuje.