StoryLark
← All guides

StoryLark Repo Connection Standard v1

Status: normative. Deliverable D3 of the content standards plan.

Repo mode lets a deployment take its content from a git repository the customer already owns. This document defines what a connectable repository looks like, what StoryLark does on each sync, and — most importantly — what it will never do.

Prerequisites: content must conform to the StoryLark Content Format; the deployment must meet the deployment requirements, in particular §1.1 on outbound HTTPS.


1. The premise

Git is the CMS. The repository is the source of truth; StoryLark is a reader of it. Content flows one way, always.

That single sentence settles most of the questions that follow. StoryLark holds a read-only token; it never commits, never opens a pull request, never writes a file back, and never asks the repository to change so that StoryLark can understand it. If a repository and this standard disagree, the repository wins and StoryLark reports what it could not use.


2. What a connectable repository looks like

There is no required folder structure. The folder tree is organisation, never meaning. Filenames are not parsed, prefixes are not consulted, and depth carries no significance.

A repository is connectable when:

  1. It is reachable over HTTPS — public, or private with a read-only token.
  2. Content files carry a storylark: block (SCF §2). Everything else in the repository is ignored, safely and by design.
  3. Ids are unique within the connection, and order values do not tie (SCF §3.3, §3.4).

That is the whole requirement. A repository can be a manuscript folder, a website with content beside its templates, or a monorepo with StoryLark content in one subdirectory — all three work, because all three are answered by the same question: which files say they are StoryLark content?

2.1 The path setting

path narrows where StoryLark looks. It is an optimisation and a scoping tool, not a safety mechanism — the storylark: block is the safety mechanism. Pointing at the repository root is a legitimate choice and ingests nothing unintended.


3. Connecting

The operator supplies:

Field Required Notes
Provider yes GitHub at v1 — see §7
Repository URL yes HTTPS
Visibility yes Public or private
Branch no Defaults to the repository's default branch
Path no Defaults to the root
Access token private repos only Read-only, scoped to this repository
Webhook secret only for push-triggered sync See §6

A public repository needs no credential. This is the cheapest way to evaluate repo mode, and it should be the path a first-time user takes.

3.1 Authentication rules

3.2 Dry run

Connecting offers a dry run before anything is ingested. It fetches, validates every candidate through the same gate a real sync uses, and reports exactly what a real sync would do — without writing anything.

The dry run is not a different, gentler check. It is the same gate. That is the only way its verdict is worth anything.


4. What a sync does

  1. Fetch the branch as an archive over HTTPS.
  2. Walk path.
  3. For every file with a storylark: block, build a candidate record.
  4. Hand every candidate to the content gate — as a set, not a stream (SCF §5), so file order never matters.
  5. Write what passed. Report what did not, with the reason and the line.
  6. Report what is in the library but was absent from this arrival (§5).

Step 4 is the whole architecture. The sync transport contains no validation logic of its own. A file rejected by a sync is rejected with the same code and the same message it would get from the portal or the API, and there is a test that asserts exactly this across all three doors.

4.1 Partial success is the normal case

One bad file does not fail a sync. The good files publish; the bad ones are listed with their reasons. A repository being edited by humans will always have something in flight, and a sync that refuses everything because one file is mid-edit would be useless.

The exception is a whole-book rule: an order tie rejects that book, because a book with an ambiguous reading order cannot be published coherently. Other books in the same sync are unaffected.


5. Deletion — what StoryLark will never do

A sync will never unpublish content on its own initiative.

A chapter in the library and absent from an arrival is reported as missing. It stays published. The sync report offers a one-click removal, and a human clicks it.

The reasoning matters more than the rule. A file's absence is not a statement. It is produced equally by an intentional deletion, a partial clone, a fetch that failed halfway, a renamed folder, a branch switch, and a force-push someone already regrets. Removing content after seeing an absence twice — an earlier proposal — infers intent from a pattern, and repeating an observation makes it a more confident guess, not a different kind of thing.

Removal, when asked for, is the ordinary recoverable delete: the library entry goes; content objects, source, and revision history remain.


6. Triggers

Trigger Latency Setup
Scheduled Daily None — on by default
Webhook Seconds A webhook secret in the provider
Sync now Immediate None — a button

The scheduled sync runs on the job every deployment already has for update checks. No new infrastructure, nothing extra to provision.

6.1 Webhooks

A webhook delivery is verified against the shared secret before it is believed. A forged or unsigned delivery is rejected and logged, and this is tested by asserting the rejection — a signature check that has only ever been tested with valid input has not been tested.

A webhook is an optimisation on top of the schedule, never a replacement for it. A missed delivery costs latency, not correctness, because the daily sync catches it. Any design where a dropped webhook loses content permanently is wrong.


7. Providers

GitHub at v1, fetched as a repository archive over HTTPS. One provider, proven end to end, rather than three shipped on assumption.

A provider is exactly two functions:

Function Responsibility
Archive URL Given repo, branch, and credential, produce a fetchable archive URL
Signature verifier Given a delivery and the secret, say whether it is genuine

That is the entire extension contract. Adding GitLab, Gitea, or Bitbucket is a driver, not a refactor — the same pattern the database and storage adapters already use. A provider driver contains no validation and no ingestion logic; if one grows some, the architecture has been broken.


8. What the operator sees

Every connection reports, always:

A silent failure is a defect. A connection that stopped working must say so in the portal without anyone going looking for it — the operator should learn from StoryLark that a token expired, not from a reader asking where chapter twelve went.


9. Boundaries

Not supported, deliberately, and each for a stated reason:

Not supported Why
SSH §3.1
Writing back to the repository §1 — content flows one way
Bespoke per-customer parsers The contract is the contract; SCF §2 replaces mapping
Inferring content without a storylark: block SCF §2
Auto-deleting on absence §5
Filename or prefix ordering SCF §3.4

Each of these has been asked for, or will be. The answer is no, and the reason is written down so the answer stays the same next time.


Found a gap? StoryLark is open source — improve these docs on GitHub.