Chapter 03Get started
Local setup
From an empty machine to the first publish, with no remote repository and no host. Three terminals, one key, ten minutes.
Requirements
| Tool | Version | Why |
|---|---|---|
| Node.js | 20 or later (developed on 22.14) | Astro 5, and the web APIs the functions use. |
| npm | 10 or later | Ships with Node. npm create and workspaces. |
| Git | any recent version | To version the site. Not needed for the local trial. |
| A Git repository reachable by API | GitHub | Only to publish online. A fake local service is provided. |
node -v # v20.x or later
npm -v
Everything works in PowerShell as well as Git Bash. Commands in this documentation use
POSIX syntax; under PowerShell replace VAR=value command with
$env:VAR = 'value'; command, and && with ;.
Three ways to start
The usual case
A new site
npm create inline@latest my-site — scaffold, theme, checks, key. That is the path described below.
To work on the tool
The reference repository
Clone the inline repository itself: it contains inline-core and a bilingual example site.
Existing Astro site
Add the integration
npm install inline-core then four route adapters. See below.
1. Create the site
npm create inline@latest martin-bakery -- \
--nom "Martin Bakery" \
--courriel contact@martin-bakery.com \
--langue en
cd martin-bakery
npm install| Option | Default | Role |
|---|---|---|
--nom | folder name | Site name, shown in the footer and the help page. |
--courriel | contact@exemple.fr | Contact address shown to the client when they need help. |
--langue | fr | Main language code. Two lowercase letters. |
The command refuses to write into an existing, non-empty folder. It produces:
martin-bakery/
astro.config.mjs inline integration, static output, i18n
package.json dev, build, serve:functions, mock:git, make:key, check
wrangler.toml host configuration (functions)
.gitignore node_modules, dist, .astro, .dev.vars…
.env.example the ONLY versioned environment file
.dev.vars local trial credentials — ignored by Git
functions/api/*.ts four route adapters
scripts/ check-html, check-locales, check-logs, check-secrets,
make-key, mock-git-api
src/
content/config.ts collection declaration
content/site.json navigation, contact details, footer
content/pages/en/home.json
components/Testimonial.astro
layouts/Base.astro
lib/locales.ts the languages of THIS site
pages/[lang]/[...slug].astro
styles/theme.css the theme — the file to adapt
.github/workflows/ci.yml automated checks
No editing logic: no overlay, no schema, no identity check, no server route. All of that
comes from inline-core, as a versioned dependency. That is the whole difference
between scaffolding and copy-paste — on the day of a security fix,
npm update inline-core is enough.
The key, shown once
At the end, the command prints the site key. It is already written into .dev.vars
for local use, but it is stored nowhere in readable form on the server: only
its argon2id hash exists. A lost key is regenerated, never recovered.
2. Start the three processes
Three terminals, at the project root:
-
The fake Git repository
bash bash npm run mock:gitListens on
http://127.0.0.1:8787and mimics the two GitHub Contents API routes actually used. Publishing writes straight into the project files, as a commit would. Development aid only. -
The build
bash bash npm run buildBuilds
dist/. The build fails if content does not pass the schema, or if adata-cmspoints at a missing key — deliberately. -
The site and the functions
bash bash npm run serve:functionsServes
dist/and/functionsonhttp://127.0.0.1:8788. This is where editing happens.
npm run dev starts the Astro server alone: the site appears,
but /api/* does not exist, so /admin cannot open a session and
editing does not work. To edit you need npm run serve:functions.
npm run dev is for working on layout, not on editing.
3. First change
- Open
http://127.0.0.1:8788/admin. - Paste the key printed at creation time, submit.
- You land on the site, with a bar at the bottom of the screen.
- Click a heading: it becomes editable, a variants toolbar appears.
- Change the text, then click Publish.
With the fake repository, publishing writes to
src/content/pages/en/home.json. Run npm run build again to see the
change frozen into the HTML — in production the host does that on its own.
The simulator reproduces exactly that journey inside this documentation: key, overlay, editing, publishing, conflict. Useful to show the tool before installing it.
Ports in use
| Port | Process | Role |
|---|---|---|
4321 | npm run dev | Astro server alone — layout, no editing. |
8788 | npm run serve:functions | Built site + functions. This is the editing address. |
8787 | npm run mock:git | Fake local Git service. Change with MOCK_PORT. |
The .dev.vars file
This is the local environment file, ignored by Git, never deployed. It is written by the create command and looks like this:
# Local trial only. Ignored by Git, never deployed.
EDITOR_KEY_HASH=$argon2id$v=19$m=19456,t=2,p=1$<salt>$<digest>
SESSION_SECRET=<at least 32 characters>
EDITOR_NAME=Site editor
EDITOR_EMAIL=contact@martin-bakery.com
GIT_PROVIDER=github
GIT_REPO=agency/martin-bakery
GIT_BRANCH=main
GIT_TOKEN=local-trial
GIT_API_BASE=http://127.0.0.1:8787
GIT_API_BASE is the line that switches to the fake repository. Removing it makes
the function talk to the real GitHub — with a real GIT_REPO and
GIT_TOKEN. Detail of every variable: Reference.
.dev.vars, .env and any real environment file are ignored by Git.
The only versioned environment file is .env.example, which contains no values.
npm run check additionally verifies that no secret ends up in the build folder.
Regenerating credentials
From the site — lost key, key to change
The key is stored nowhere in readable form: only its argon2id hash lives in an environment variable. So it is not recovered, it is regenerated — deliberately.
npm run make:keyThe command prints the new key, its hash and a session secret. Nothing is written to disk: whatever is not copied at that moment is lost. The rest — replacing the variables, redeploying, handing over — is in Security and authentication.
The command only ships with the site as of that version; before it, it lived in the
reference repository alone. Three lines catch it up: copy
scripts/make-key.mjs from a recent site, then
npm pkg set scripts.make:key="node scripts/make-key.mjs" and
npm install --save-dev @noble/hashes — the hashing library does arrive in the
tree through inline-core, but nothing guarantees it is hoisted to the root.
Until then, rotation remains possible from the reference repository: same hash, same
parameters.
From the reference repository — preparing a site
# Full credentials for a site: key, hash, session secret, variables, checklist
npm run create:site -- --nom "Martin Bakery" --depot agency/martin-bakery
# Same, plus writing a ready-to-use local .dev.vars
npm run create:site -- --nom "Local trial" --ecrire
create:site prints two blocks that do not travel together: what
goes to the client (a key, an address) and what goes to the host (the hash, the secrets, the
token), plus the pre-delivery checklist. See Operating.
In an existing Astro project
-
Install the package
bash bash npm install inline-core -
Declare the integration
astro.config.mjs js import { defineConfig } from 'astro/config'; import inline from 'inline-core/astro'; export default defineConfig({ output: 'static', integrations: [ inline({ locales: ['en'], // reference language first support: { email: 'contact@agency.com' }, // shown to the client // theme: 'src/styles/theme.css', // default // pages: { admin: true, help: true }, // false to provide your own }), ], });The integration adds
/adminand/aide, builds the overlay, injects the editing bootstrap, createssrc/media/library.tsif missing, and refuses to start if output is not static. -
Write the four route adapters
The host discovers routes from the
/functionstree, outside the Astro build: an integration cannot inject them. Twenty lines, written once.functions/api/save.ts ts import { createSaveRoute } from 'inline-core/server'; import { LOCALES } from '../../src/lib/locales'; const route = createSaveRoute({ locales: LOCALES }); export const onRequest = route.onRequest; export const onRequestPost = route.onRequestPost;The other three are identical with
createAuthRoute(),createContentRoute({ locales })andcreateUploadRoute(). Detail: Route reference. -
Declare the schema and the theme
src/content/config.ts ts import { defineCollection } from 'astro:content'; import { glob } from 'astro/loaders'; import { pageSchema } from 'inline-core/schema'; const pages = defineCollection({ loader: glob({ pattern: '**/*.json', base: './src/content/pages' }), schema: pageSchema, }); export const collections = { pages };src/layouts/Base.astro astro --- import 'inline-core/styles/tokens.css'; import '../styles/theme.css'; --- -
Generate a key
Without
EDITOR_KEY_HASH, editing is impossible — that is the default state. See Security.
Working on inline-core itself
The reference repository is an npm workspace: packages/inline-core and
packages/create-inline are installed by link, and the site at the root consumes
them directly.
git clone <repository> inline && cd inline
npm install
npm run create:site -- --nom "Local trial" --ecrire # writes .dev.vars, prints the key
npm run build
npm run serve:functions
npm test # the whole suite: git, auth, hardening, bootstrap, sanitising, media…
npm run check # raw HTML, language parity, logs, secrets
To test the packages exactly as they would ship to a registry, see
Operating (test:scaffold and
test:pack).
If it does not start
| Symptom | Likely cause |
|---|---|
/admin returns 404 | You are on npm run dev without building, or the integration is not declared. |
| The key is refused although it is right | .dev.vars missing, or EDITOR_KEY_HASH empty. Variables are only re-read on (re)start. |
| “Incorrect key” after five attempts | That is the rate limit: 5 attempts per quarter of an hour. It works. |
| Publishing fails locally | npm run mock:git is not running, or GIT_API_BASE is missing from .dev.vars. |
| The image does not appear after publishing | The file is in the repository, but the HTML has not been rebuilt. Run npm run build again. |
| Error “Charte introuvable” (theme not found) | src/styles/theme.css is missing, or the theme option points elsewhere. |
Full list: Troubleshooting and known traps.