EN FR

Chapter 02Understand

How it works

Two flows, two coupling points, eleven rules that are not up for debate. Enough to read the code without getting lost, and to know straight away where a change belongs.

5 min read12 sectionsChapter 2 / 22

The read flow

This is the visitor's flow, and it is deliberately short: everything happens at build time, nothing at runtime.

content JSON Astro (build) static HTML CDN visitor

A visitor receives an already complete HTML file. No network call fetches content, no database is queried, and JavaScript is required for no displayed word. A public page carries about 175 bytes of inline script, which read a cookie and do nothing else — see “the editing bootstrap” below.

The write flow

This is the flow of a client changing their page. It is longer, and every step has a role.

overlay POST /api/save rate size identity Zod schema lock commit rebuild
  1. The overlay sends the whole page content

    Not one field, not a diff: the entire JSON file as it should be after the change, along with the version read when the page was opened.

  2. The function refuses in a precise order

    Rate, then declared size, then identity, then received size, then shape, path, schema, lock, write. The order is not cosmetic: a size cap placed after reading the body protects nothing, and refusing a caller who keeps hammering does not require knowing who they are.

  3. The same Zod schema as the build validates the content

    Not a copy of the schema: the same object, imported from the same file. Content that would fail the build never enters the repository.

  4. The optimistic lock compares versions

    If the file changed in the repository since the page was opened, the write is refused with an explicit conflict — never a silent overwrite.

  5. A commit, then a rebuild

    The commit is attributed to EDITOR_NAME / EDITOR_EMAIL. The host detects the push and rebuilds the site: thirty to sixty seconds.

The repository is split in two

This is the decision that keeps operations sustainable beyond three clients. What is identical from one site to the next is shared and versioned; what belongs to the site is copied then adapted.

WherePer site
Overlay, content model, components, server routes, security packages/inline-core versioned dependency
Content, theme, layout, languages, route adapters the site project created once, then adapted
Rule

Anything in inline-core is never copied into a site. A business rule that shows up in /functions/api is a rule in the wrong place: on the day of a fix, it will have to be found again in ten repositories.

The two coupling points

Two things, and only two, depend on an outside provider. They are isolated behind an interface, in two files:

the only two coupling points text
packages/inline-core/src/server/git-provider.ts   readFile / writeFile        (GitHub | GitLab)
packages/inline-core/src/server/auth.ts           verifyAuth / createSession  (site key | delegated)

No identity check and no Git API call exists anywhere else in the code. That is what makes it possible to change host in a few hours, to move from GitHub to GitLab by writing one implementation, or to switch to multi-user authentication without touching the rest.

The eleven absolute rules

They take precedence over everything else. They are repeated here because they are rarely broken out of conviction, and often out of reflex.

#RuleWhy
1output: 'static' — never SSR, never hybridIt is what guarantees all content is in the served HTML.
2No editable zone inside a hydrated componentclient:only removes content from the index; client:load re-renders it on load and wipes the edits in progress.
3The Git token never leaves the serverIt belongs to the agency, not the client. Never in a response, a served file or browser storage.
3 bisThe site key is verified server-side onlyA hash served to the browser can be attacked offline.
4No secret in the repositoryEnvironment variables of the server function, and nowhere else.
5No free-form stylingStyles go through the Zod enums. No hex, no pixels in content.
6Do not use src/pages/api/*In static output those run at build time, not per request. Everything goes in /functions.
7Every write is validated server-sideIdentity, schema, path, size, sanitising. Client-side validation counts for nothing.
8No technical jargon in the interface“Publish”, not “Commit”. The client will never see the code.
9No content tree in the overlayThat is the moment the tool becomes a CMS again and the client gives up.
10Videos are never uploadedA heavy file in Git breaks the repository and the builds. Provider + id, nothing else.
11Do not widen the scopeOut-of-scope requests are flagged, not implemented.

Four mechanisms worth knowing

The schema lives in a neutral file

The write function cannot import astro:content: it runs outside the Astro context. So the Zod schema is isolated in inline-core/src/schema.ts, which imports nothing from Astro. The build and the write validate with the same object, not with two copies that would eventually diverge.

The editing bootstrap, and why it grants nothing

Every page carries about 175 bytes of inline JavaScript: it reads the inline_edit cookie and, if present, appends a <script> tag pointing at the overlay. A public page therefore never downloads the editor.

The marker is forgeable, deliberately

The inline_edit cookie contains no secret. Forging it only shows an interface whose writes will all be refused server-side: routes check the signed session cookie, never the marker. Editing the DOM without a key is no more consequential than using the browser's developer tools.

The optimistic lock

On open, the overlay reads the file through /api/content and receives a version — on GitHub, the blob SHA. That version travels back when publishing. If it no longer matches, publishing is refused and a message invites a reload. Nothing is overwritten, and the local draft is not lost.

Verifying a key is expensive on purpose

About 350 ms: that is the cost of argon2id at recommended parameters, and it is what makes brute force impractical. That cost is paid at sign-in only, once per 8-hour window. On a host billing CPU time, check that your plan covers that duration on /api/auth.

The four routes

RouteRoleIdentityRate
POST /api/authOpens a session from the key5 / 15 min
GET /api/contentReads the repository state and versionyes60 / 5 min
POST /api/saveValidates and publishes a pageyes30 / 5 min
POST /api/uploadStores an image in the repositoryyes30 / 15 min

Full detail of requests, responses and error codes: Route reference.

What does not exist

  • No database. The Git repository plays that role.
  • No application server. Four stateless functions, called on write only.
  • No server-side session. The cookie is signed; nothing is stored.
  • No user accounts. One key per site.
  • No cache to invalidate. The HTML is rebuilt, therefore replaced.

Next, in practice: Local setup.