Problem
Our RFCs live in two places that do not agree.
The site's Lab is a build step over docs/rfcs. One flag decides who reads a page:
public: true renders it for everyone, public: false renders it into an admin-only draft.
There is nothing in between, so the pitch documents (0023 to 0026) are shown to investors and
sales contacts through separate rooms, and nothing else can be shown to a partner without
making it public.
The RFCs product (RFC 0035, named in RFC 0054) stores spaces, documents, versions, comments,
reviews and diagrams, and its reading and editing tool is being built (RFC 0062). It has no
anonymous read path. Its documents keep a public flag, but nothing reads it when deciding
access: a document is readable by its account's members and by people named on it (RFC 0041),
and InOrbit's own accounts are readable by admins only. A partner who signed the NDA
(RFC 0056) or holds the partner role (RFC 0057) reaches none of our documents through the
product, even the ones the site already publishes.
The console's import reads files in the browser, renumbers documents (0040, 0053 and 0059 are
not in docs/rfcs, so every later number shifts) and resets their statuses. Importing our own
RFCs that way would break every link to them.
Proposal
Five access levels
Every document has exactly one level. The same rule decides on the site, in the console, in the API and in the MCP tools, because all of them read through the labs service.
| Level | Who reads it |
|---|---|
public |
Anyone, signed in or not. Indexed by search engines. Only after the redaction check passes. |
preview |
People with the preview role (an NDA signed, RFC 0056), partners and admins. |
partner |
People with the partner role (RFC 0057) and admins. |
team |
The space's account and the people named on the document (RFC 0041). |
internal |
Admins only. |
- A new document starts at
team. Existing documents mappublic = truetopublicand everything else toteam. Thepublicfield stays for one release, derived from the level, then goes. - InOrbit's own accounts keep their rule for
teamandinternal: admins only. A document in them atpublic,previeworpartneris readable at that level. - Writing does not change: members write, owners and admins manage, people named on a document do what their role on it allows.
- One function decides every read: get, list, search, timeline, export, versions, comments,
diagrams and mentions. A document the caller cannot read is
NOT_FOUND, and its title never appears in another document's list of mentions or backlinks.
A diagram is drawn here in the RFCs product; this page does not show diagrams yet.
Changing a level
SetAccess(document, level) is open to the space's managers (owners and admins of its
account). Raising a document to public runs the redaction check of iohr lab check on its
current text; any finding refuses the change with FAILED_PRECONDITION and the findings' rule
ids and lines, never the text. Every change writes an audit line: who, the document id, the
level before and after.
The public read path
Three routes read without a token:
GET /v1/rfcs/public/documents, the readable documents, paged by the response contract (RFC 0033), narrowed to one space when asked;GET /v1/rfcs/public/spaces/{space}/{kind}/{number}, one document by its space slug, kind and number, optionally at a version;GET /v1/rfcs/public/spaces/{space}/diagrams/{id}, a diagram, only when a readable document in that space embeds it.
On the API host they are open. On the site's hosts a signed-in reader's verified claims are
forwarded, so the same routes also return what that reader's role allows. Every route has a
rate limit per client address. Everything else under /v1/rfcs keeps its token requirement.
A diagram is drawn here in the RFCs product; this page does not show diagrams yet.
Our RFCs in the product, numbers kept
An admin-only ImportDocuments call upserts documents by space, kind and number. It keeps the
number, the status, the date, the status log, the front matter and the parts, maps public: to
the access level, and records the first version as imported from the repository at a named
commit. Running it twice changes nothing.
- The Engineering account holds the spaces: Platform, LLM and EVM; Pitch at
partner, matching today's rooms, once the owner confirms; Quiet Pager atinternal. mise run rfcs:importandmise run rfcs:exportmove the documents in and back out.- During the transition a merged change to
docs/rfcsis imported, so the current way of writing an RFC keeps working. When the console editor (RFC 0062, phase 2) is in use, one change makes the product the source and the repository its export. This amends RFC 0035, where the repository is the source of truth. - Studies follow, with their data folders as attachments, so their charts render from the API.
A diagram is drawn here in the RFCs product; this page does not show diagrams yet.
A host for the company's RFCs
The company's RFCs are read on a host of their own, rfcs. under the company domain: our
own published space in the sense of RFC 0039.
- The site's build writes a static page for every public document from the API, with a
sitemap entry, structured data (
TechArticle) and a preview image. If the API cannot be reached the build fails, except in CI, which uses a committed fixture. - In the browser the page asks the API for the current version and replaces the snapshot when a newer one exists. A signed-in reader also sees the documents their level allows, rendered in the browser only and never indexed.
- The reader is shared with the console's reading view (RFC 0062): a contents panel that follows the scroll, jumps to the usual sections, links on every heading, a status timeline from the status log, the pull requests that implement the document and their proof (RFC 0041), versions with a diff, mentions, diagrams drawn from the diagram model, charts, print, dark mode and a phone layout.
- The personal site's
/lab/stays as it is.
Team and Company, and a site per person
- The site's header switch reads Team and Company. Team lists the people, the owner first, each linking to their own site.
- Each person gets
<name>.inorbit.hr. Adding a person is one entry in the site's people list and a DNS record. - Bare
inorbit.hrbecomes the company home when the owner opens it. Until then it keeps the door (RFC 0057), and the personal pages move to the owner's own host.
Controls
- A new public read path. The three public routes are the first in the RFCs product that answer without a token. They read only; on the API host they carry no identity; on the site's hosts identity is still verified by , never by the service.
- Default deny. Every level other than
publicneeds a verified caller with the matching role or membership. A new document starts atteam. Anything unreadable isNOT_FOUND. - Redaction before public. No document becomes
publicwith a finding of the redaction check, the same check the site's build runs today. - Audit. Every access change is logged with who, the document id and both levels, never the document's text. Reads keep the existing who, what and outcome lines.
- Availability. The public routes have a per-address rate limit at the edge, and their pages are bounded by the response contract.
The compliance registry records the new public read path and per-document access.
Alternatives considered
- Keep the
publicflag and add a partner list per document. Every document would carry its own reader list, and changing who counts as a partner would mean editing every list. Levels tied to roles change in one place. - Serve the site's RFC pages from the build only. Simple and fast, but a document changed in the product would wait for the next site build, and a signed-in partner would see nothing more than an anonymous visitor.
- Make the site read the API at request time with no snapshot. Search engines and readers on a slow connection would depend on the API for every page view. The snapshot keeps the pages static; the live refresh keeps them current.
Decision
Open. The owner set the direction on 2026-10-06: five levels, the product as the source, the RFC host built as a snapshot and refreshed live, and a site per person.
Publication
The RFCs reference gains the public routes and SetAccess. The console shows each document's
level and lets a manager change it. The site's RFCs move to the RFC host as each step
ships.
Status log
- 2026-10-06: opened. Delivery in this order: the RFC; access levels and the public read path; the import with numbers kept; the RFC host with the shared reader; diagrams for the first RFCs a visitor meets; the Team and Company switch and the first personal host; studies and the switch of the source.
- 2026-10-06: access levels and the public read path built, not yet rolled. Every document
has one level (migration
20261006174510_labs_access; existing public documents stayed public, the rest became team), every reader one clearance in its space, and one function decides every read.SetAccessis for the space's owners and admins, refuses public on a redaction finding by rule and line, and is audited with both levels. The three public routes answer with no token on the API host and with the reader's sign-in on the site, rate limited per address. The console shows each document's level and lets a manager change it. Preview and partner are settable only in InOrbit's own spaces; a customer's documents are never read through a platform role. - 2026-10-06: classified spans cut on the server. Every place document text leaves the labs service (reads, versions, timeline, comment quotes, the export, the MCP tools and the public reads) cuts the spans above the reader's clearance with the site's grammar, one ladder for documents and spans. Markers that do not parse are refused on save and never served; the public check reads the public cut; search and mentions never reach inside a span.
- 2026-10-06: the remaining text exits follow the cut. The timeline never indexes a line inside a classified block; a comment quote keeps only what occurs in its reader's own cut of the quoted version; a version's size is the size of the reader's cut; and an older version is read through the current version's spans, so a fact classified later stays withheld in the history for every reader not cleared for it.
- 2026-10-06: the import built, not yet run. An admin-only
ImportDocumentscall bringsdocs/rfcsinto the Engineering account's spaces (Platform, LLM, EVM, Pitch, Quiet Pager, made when missing) with numbers, parts, slugs, status and front matter kept; a version is added only when the text changed, recorded as imported at the commit, so a second run changes nothing. Pitch lands at partner, Quiet Pager at internal,public: falseat team, andpublic: trueat public only when the public cut passes the redaction rules and the strict rules, else at team with the findings by rule and line. Raising a document to public by hand and serving it publicly now run the strict rules too in our own spaces.mise run rfcs:importandrfcs:exportmove the documents in and back out. - 2026-10-06: the import ran against production after the read paths were made to cut every exit (#459: the timeline, comment quotes, version sizes and older versions) and the owner-reviewed sweep withheld deployment details in the public RFCs (#402). 94 documents at 26e57bda: 76 public, 10 team, 4 at partner (Pitch) and 4 internal (Quiet Pager), numbers and parts kept. Checked as an anonymous reader: 76 listed, no Pitch document, no classified marker in any response, and RFC 0010 served with its withheld spans as bars. A second run reports every document unchanged.
- 2026-10-07: the company's host, phase 1, built.
rfcs.under the company domain serves a static snapshot of the public read path, taken when the site is built: an index by space and status, a page per space and per document at/<space>/<number>-<slug>/, a sitemap and robots file of its own, structured data per document and canonical addresses on the host. The build cuts each text again and holds it to the redaction and strict rules; a document that fails is left out. A build that cannot read the product fails, and CI uses an invented fixture that says so on every page. Not yet: the refresh after load, the signed-in levels, the shared reader, diagrams and a preview card per document. - 2026-10-07: Diagrams kept in the repository (owner: every RFC that describes a flow,
an architecture, a lifecycle or a decision gets diagrams). Models live in
docs/rfcs/diagrams/<space>/,mise run rfcs:diagramsupserts them by name over the diagram API (a new version only when a model changed) and puts their ids into the texts, andrfcs:diagrams:checkholds the ids and the public rules in CI. No API change was needed. - 2026-10-07: Checked: #398, #410, #459 and #471 are live; the import ran. Open.