The operating manual for traveller.org — kept current with every publish that changes a process. Sits with the CMS portal behind the admin door.
traveller.org is a static website generated from plain content files. There is no database and no server-side code: every page is pre-built into HTML whenever content changes, then served from a CDN. Publishing means "commit a change to the GitHub repository" — everything downstream is automatic.
CLAUDE.md in the
repository) make the rule binding on them too.legacy/, the crawl of the old site, and it
used to recognise only two album layouts — so anything shaped differently fell through
in silence, and 440 real pages were returning 404 on the live site: paginated album
indexes, per-note currency pages filed a level deeper than their siblings, the 1992 Africa
letters. The crawl is the checklist now: a page in it with nowhere to go fails the
generator, and a build missing one fails tests/redirect-stubs.test.mjs. Three
kinds of page are exempt, on purpose — ones the old site had itself stopped serving,
addresses the new site answers directly, and query strings the crawler flattened into
filenames, which were never paths anyone could request._redirects file gives a proper 301 — but it honours only about 100 rules
and cannot read a query string, so two sets of URLs were being forwarded the weak way: the
618 exact old addresses (a page that says “moved” in the browser, which Google
treats as a soft redirect worth far less), and every journal post, because the
old site’s posts were query strings — /journal/?p=15 was
“Climbing the Pyramids”, the most-read page on the whole site. After the cutover
those served the journal index, told Google the index was the canonical page, and only
JavaScript carried a reader to the post. A small program that runs in front of the site
(functions/_middleware.js) now answers all of them with a true 301. The
“moved” pages are deliberately kept underneath it: if that program is ever
switched off, every old address still forwards, just less strongly.The stack table below says what each service is. This one says what is
true right now, because during the cutover those are different things and the table
alone reads as though everything is already in place. Every line is dated; a session
updates this section in the same push as any change to the domain or a service, and
tests/docs-refs.test.mjs keeps its runbook step numbers honest.
near-dupes/, named in the email,
reversible with direction: keep-near.| What | Today | Becomes |
|---|---|---|
| The live site | Cloudflare Pages — the cutover happened on 2026-08-19. www.traveller.org
answers server: cloudflare with a CF-RAY and serves the new site,
built from the production branch and current.
Since 2026-08-30 the site has ONE address: the bare traveller.org
forwards to www with a 301 instead of serving a second copy — cookies
and stored logins are per-host, so the apex was the whole site with none of your keys,
and that is how a signed-in owner came to see no banner and no hidden albums
(mail is untouched; MX is DNS, not HTTP) — verified the same
day, counting 60 countries on the live home page against 60 in the data.
Since 2026-09-09 the host it runs on is in maintenance mode: Cloudflare has moved
all new development to Workers and Pages now only receives upkeep. Nothing is
broken and nothing has a deadline — Pages still builds and serves, with no announced
shutdown — so the site stays put; you chose on 2026-09-09 not to spend development
time on it. What a move would involve, and why the Spec Intake form should rehearse it
first, is on the runbook’s open list. | Nothing further for now. It updates whenever Publish is pressed. One day, a move to Workers — when there is a reason, not because Pages stopped working. |
| The friends door | Built 2026-08-27; the owner set FRIEND_KEY and switched it on
2026-08-28. One shared friend password
(FRIEND_KEY on the Cloudflare Pages project) opens every unlisted
album’s pages on www.traveller.org; friends sign in at
/friends/, land on the home page under a signed-in banner, and stay signed
in about a year per device; hidden albums appear in the country timelines for them, and
friends-only photographs carry a Friends Only flag. The gate is the Pages
Function in front of the live site, so it exists only there: the test site is plain
files with no door, and the photograph files on images.traveller.org remain
public addresses (the 2026-08-19 call, unchanged). With no FRIEND_KEY set the
gated pages answer “switched off” — closed, not open.
The same door also covers single photographs: any photo of a public album can be
marked friends-only from Gallery cleanup — off the public page and every
count, woven back in for signed-in friends.
Since 2026-08-30 the Photographs page (/galleries/) lists every unlisted
album for a signed-in friend or admin, country-named cards under the public listing
— before that a country’s own page was the only page that could show one, and
an unlisted album looked lost from everywhere else. The same push fixed the silent way
those cards could fail to draw: a workers.dev Worker address remembered by an old build
is now dropped on the public pages exactly as the admin login drops it.
Since 2026-09-02 an iPad held upright shows the album cards side by side —
the meta column beside the four-photograph strip, as a desktop does. The phone layout
(details stacked above the strip) used to begin at 900px, which is wider than every iPad
portrait, so a tablet got a full-width band of empty cream above each strip; the stack now
begins at 560px, where the thumbnails beside the details column would genuinely be too
small — and there, stacked, the card’s details are a compact header with the
Gallery link beside them (~80px) rather than a floor of empty cream above each strip.
Since 2026-09-01 a film can be unlisted, exactly as an album can: off the films
pages, the home strip, every count and every feed, while a signed-in friend sees it on the
films pages under a purple ⊘ Unlisted poster chip and plays it there. Set at upload (both
film forms have the switch) or any time in Gallery cleanup → Films. The film’s
.mp4 keeps its public storage address, the same rule as photographs — unlisted means
undiscoverable, not unwatchable, and that trade was the owner’s call.
Since 2026-09-01 the old shop’s photograph pages land on their country rather
than on the home page: 43 addresses under /store/cart/ whose filename names a
country (gua-yellow-wall, nepal-bhaktapur-boy) now forward to that
country’s page. A product page forwarded to a home page is what Google calls a soft
404, which is why they kept coming back in the indexing report. The dozen that name no
country — the shop indexes, bodie-table, wedding-day —
still land on the home page, there being nothing truer to send them to.
Since 2026-09-01 the banknote band on a country page holds its notes in a row at every
width. On a screen between roughly 760px and 1050px — a laptop, a landscape
tablet, a browser window that is not full-screen — the notes were being squeezed into
a column one note per line beside the heading. Every euro country had it, because the
“Shared currency · … since 2002” line takes width on the same
line; on Spain at 764px the notes were left a strip 17px wide. The notes now ask for the
width they need and the band wraps around them, which is what the phone layout already
did. | Nothing further — the manual’s Friends section says what to send people, and rotating the password is one edit in the same Pages setting. |
| The phone uploader | Since 2026-08-30 its album list is complete. The Worker finds unlisted albums by
walking the bucket, and it read only the first page of each listing — so countries
late in the alphabet were never walked and their unlisted albums were missing from the
picker (and from Gallery cleanup’s droplists) while opening perfectly on the site.
Fixed by following the cursor to the end of every listing.
The upload Worker serves a phone-built page at /m since 2026-08-24
— searchable album picker, album creation in the flow, and upload-then-import as one
action. Phones opening the upload page land there automatically; the desktop form is
unchanged and always reachable at ?stay=1. Deploys from the trunk on every
push, like the rest of the Worker.
Since 2026-08-27 an upload with no file extension on its name is named from its own
contents — the share sheet does not always hand a photograph to the Shortcut with
its filename intact, and four photographs of Madrid reached storage as
IMG_5064…IMG_5067, were counted as stray files and were
published by nothing while the run reported success. Corrected as the object is stored, and
repaired at import for anything already in the bucket. See
the pipeline page.
Since 2026-08-27 it also carries a way through to the admin tools — a
Publish to the live site card at the foot of the home screen, below the Upload
Photos button, with Gallery cleanup, Cards & strips, Banknote pages and this manual
beside it. They point at the staging
admin deliberately: that is the master set, and the live copies sit behind Cloudflare
Access. | Nothing further — it is live. The manual’s pages about it reach production on the next Publish. |
| The test site | Unchanged: GitHub Pages at brarob100.github.io/traveller.org takes every commit,
and traveller-org.pages.dev still answers as the Pages project's own address.
Both are useful — one shows the newest work, the other shows exactly what the public
sees. Its admin pages are the master set (2026-08-22): they act on the same storage
and the same pipeline as the live ones, they carry the newest controls, and the album list
every admin page shows — live one included — is read from here. See
Staging admin or live admin? | Stays. Neither is going away at cutover. |
| Visitor statistics | Google Analytics 4, property G-80N129L5BJ, live since 2026-08-26
on the public pages of the live site only — not /admin/ (the tools
and this manual), not the test site, not the 622 old-URL forwarding pages. Any GA figure
dated before 26 August is measuring the old WordPress site; the page titles say
which is which (| Traveller's Tales - The Journal is the old one). | Nothing further — it is live. Chosen over Cloudflare Web Analytics for continuity with the old figures. See the pipeline page. |
| DNS | Cloudflare, zone Active since 2026-08-19. Porkbun is the registrar and
nothing more; no record is ever created there again. Three names must stay
grey-clouded (DNS only) — mail, cpanel,
ftp — because Cloudflare's orange cloud proxies HTTP and HTTPS only.
That trap was hit and repaired the same day: the import left the apex MX pointing at
the proxied apex itself, which silently stops inbound mail. | The 600-second TTL goes back up at step 25, once the dust settles. |
| Photographs | images.traveller.org is attached to the bucket and works — a
thumbnail answers 200 over it, verified 2026-08-19. The live site is still asking the old
address. PUBLIC_R2_BASE is reported set, but the build that is serving was
made before it took — a variable is read at build time only. Nothing is broken
meanwhile: both addresses serve the same bucket. | Every image request on images.traveller.org, one green build after the
variable lands, finishing step 14. |
Still InMotion, forwarding eight @traveller.org addresses to Gmail.
The apex MX now reads mail.traveller.org, a grey-clouded A record at
216.194.165.246 — written 2026-08-19 to repair the proxied-MX fault above.
Cloudflare Email Routing is not switched on: it refuses to activate while a
non-Cloudflare MX sits on the apex, and that record is the one it means. Resend's MX
on send.traveller.org is a different hostname, is not the conflict, and must
not be deleted. | Cloudflare Email Routing for receiving (step 15) and Resend SMTP for sending (step 16). | |
| Contact form | Live on www.traveller.org (checked 2026-08-20: the page carries the
real site key, the Turnstile script and the live endpoint, and the contact Worker answers
past its secret check). And now on staging too — the staging build was the one
still missing both public keys, which is why brarob100.github.io showed the
grey “Test build” line and a drawn checkbox that cannot be clicked. That
is a build that never saw the keys, not a broken CAPTCHA. | One dashboard step for staging: Cloudflare → Turnstile → the
traveller.org contact widget → Settings → Hostname
Management → add brarob100.github.io. The widget renders only on
hostnames it lists; without this it is a blank space where the checkbox should be. |
| Search & answer engines | Switched on and correct (2026-08-19). robots.txt allows everything
except /admin/ and names the sitemap; llms.txt and
llms-full.txt are served and every link in them reads www.traveller.org.
No AI crawler is blocked, deliberately — the llms files exist to be read, and
blocking the crawlers that read them is the one change that would cost the site its answer-
engine visibility. Checked against the live host, not the build. | Nothing further. Step 22 tells Search Console, which is confirmation rather than introduction. |
| Admin on the phone | Gallery cleanup and Cards & strips work one-handed since 2026-08-25: below
phone width, Gallery cleanup becomes an album picker plus Browse / Select / Arrange modes
with slide-up sheets, and Cards & strips becomes a hub of three doors with one Save bar
that follows you and names what is unsaved — both built from the approved boards
(design/ios-admin/). Reordering was flatly impossible on iOS before: the
desktop pages drag with a mechanism iPhones do not have. Same machinery, same refusals;
the desktop rendering of both pages is untouched. | Nothing further — the manual pages describing both reach production on the next Publish. |
| The duplicate check | Site-wide from 2026-08-25, and switched on album by album. Every import now
stores a fingerprint of each photograph it publishes (in the bucket, under
hashes/) and compares its arrivals against every fingerprint on file — so
a photograph already published in another album is named in the import email.
Nothing is deleted across albums, by design. An album that has not been fingerprinted
yet takes no part in the check and the run log says so. | Done for the exact check: the backfill ran on 25 August 2026 and fingerprinted
23,997 photographs across 210 albums. It found 622 published in more than one
album (nothing deleted — see the import email and
the pipeline page) and none
doubled inside one album. Open, and worth a look: reading every photograph turned up 274 that the site asks for at an address where nothing answers — broken pictures on the site today, none of it caused by the fingerprinting. Three separate reasons, each with its own fix: 186 in srilanka/2005 are filed in a place folder while the record and
the page both ask for the flat address (the same photographs also publish correctly as
srilanka/2005/colombo); 78 in turkey/2022,
cambodia/2009/siem-reap and belgium/2016 were rotated again and
the record still names the previous version of the file; 10 are simply not in
storage. A sync run per album settles the last two, and it is your call, because it
takes the missing ones out of the album’s count. |
| The upload Worker | Answers on this domain since 2026-08-20, at www.traveller.org/api/
— a Workers route, no DNS record, created by the deploy that ships its code. Verified
rather than assumed: /api/galleries refuses a wrong passphrase with
403, which is the Worker itself talking. Its own
traveller-upload.brarob.workers.dev address still works and still carries the
iPhone Shortcut. The first attempt was refused because the deploy token could write
Workers Scripts and not Workers Routes; the owner added
Zone · Workers Routes · Edit and the next push created it.Since 2026-09-14 it also answers at upload.traveller.org — a plain
address to type on a phone or hand to somebody helping with a batch. A Worker custom
domain declared in wrangler.toml, so the deploy created the DNS record and
the certificate itself; the dashboard cannot do this at all, which is what the
2026-08-20 attempt ran into. Verified the same minute, not assumed: the name resolves to
Cloudflare, / and /m both serve the uploader carrying this
deploy’s own build stamp, /galleries answers 403 without a
passphrase (the Worker itself talking), and the certificate verifies. All three addresses
are live and none replaced another: /api/ is still what the admin pages
call and still the only same-origin one, and workers.dev still carries the
iPhone Shortcut.The route exists so the admin pages call it on a hostname that resolves: after the Access lockdown every interactive one went blank on “Failed to fetch”, and staging then sat for minutes on end with empty panels — the same workers.dev hostname, black-holed rather than refused. The staging build now
points at www.traveller.org/api too, which is the same Worker by a name
that answers. | Access must never cover /api/*, or the pages break again. If a
browser still holds the old address, press Sign out on the admin login and connect
again — a remembered address wins over the offered one, deliberately. |
| The admin pages | Behind Cloudflare Access since 2026-08-19 on
www.traveller.org/admin* (owner; verified again 2026-08-20 and 2026-08-26 —
an anonymous request is answered with a 302 to the one-time-PIN login, not a page), with the
upload Worker behind them passphrase-gated and rate-limited on top. A consequence worth
knowing when something looks wrong: a script or a session checking whether a change
reached the live manual is reading the body of that redirect, not the page — which is
exactly how a landed deploy was once reported as stuck (2026-08-26).Work on the live domain, not on staging, when a browser is fussy. Staging is on github.io, so every call it makes to the Worker is cross-site —
there is no same-origin route to offer and GitHub Pages cannot have one. A browser that
refuses cross-site requests therefore refuses all of them, whatever address is typed: on
2026-08-20 the owner's browser answered the Worker perfectly in a tab and blocked the same
call from the staging page at the same moment. On www.traveller.org/admin/ the
call goes to /api on the same name, and there is nothing cross-site left to
refuse. | Fast again since 2026-08-20 (evening). The week's slowness had one active cause
left after the address moved to /api: the Worker's /galleries
endpoint — which every interactive page asks on connect — was reading the entire
photo bucket object by object, ten to twenty seconds a call. It walks directories in
parallel now and answers in under a second; the Pipeline
page has the mechanics. Access was never the cause: it has never covered
/api/*, and the pages were loading their data the whole time — late.
The PUBLIC_UPLOAD_URL variable on the Pages project no longer matters either
way: every page on this domain now prefers its own /api address in code. |
| DMARC reports | Watched, not monitored, since 2026-09-10 — the switch is thrown. The
policy published on 19 August 2026 is p=none — monitor only, nothing is
blocked — and the daily reports have been arriving at dmarc@traveller.org
ever since. The first five (31 Aug – 3 Sept, Google and Outlook, 24 messages) were
read on 2026-09-10 and were entirely clean: every message that claimed the domain
proved it, from just two senders — Resend (import notices, contact form) and
Cloudflare Email Routing forwarding to Gmail.On 2026-09-10 the owner re-pointed dmarc@traveller.org from their Gmail
to the traveller-dmarc Worker (that rule had forwarded to Gmail since cutover
step 15) and set the Worker’s RESEND_API_KEY. So the reports no longer
land in the inbox: the Worker reads each one and writes only when something needs acting
on, forwarding the original report alongside the alert. Silence from it means checked and
clean. | Nothing, until it speaks. The Worker’s request count on
its
own page shows it being invoked in the meantime, which is what distinguishes “quiet
because clean” from “quiet because nothing is arriving”. After a few quiet
months the accumulated evidence supports moving the policy from p=none to
p=quarantine, which is what actually stops somebody sending as this
domain. |
| The guides shelf | Live and open since 2026-09-05 — no door, by the owner’s choice. www.traveller.org/guides/ is the shelf of end-user guides for
every app on the domain.
Since 2026-09-06 every app but one has its guide: the owner asked for first
versions of all of them in one pass, and seven were written from their repositories’
own documentation and code — 26 pages — each app’s pages living in its
own repository and synced onto the shelf within a minute of the push, which also proved
the whole pipeline end to end (the read token reached all seven private repositories;
the dispatch ping fired from every one). The exception is contacts, whose
repository is empty — nothing exists to document accurately, so it stays listed
and waiting. (apartment-alerts came off the list earlier the same day, the
owner’s call — no longer part of the documentation project.)
and waiting for their first page. The shelf is unlinked from the public site,
noindex, disallowed in robots.txt and absent from the sitemap,
but anyone with the address can read it — every guide is written as if public.
The Cloudflare Access application for www.traveller.org/guides* was designed
and deferred (owner, 2026-09-05); it sits on the runbook’s open list.
Since 2026-09-07 an app's own documentation reaches the live shelf by itself
— the owner's golden rule: a push to any tracked app's docs/guides
pings this repository, the sync copies the pages in, and it then publishes them to
production without anyone pressing anything, usually within a few minutes. It publishes
only when those guides are the sole thing waiting on the trunk; anything else
pending and it stops at staging and says so, since a publish moves the whole trunk head.
That is the one standing exception to “nothing goes live until the owner says
publish it”.
The shelf has a door, since 2026-09-14. A second Cloudflare Access application
covers www.traveller.org/guides* — separate from the admin’s, with
its own allow-list of two: you and Reynald, by one-time PIN. Verified the same day from
outside: /guides/ and every page under it answer with the login instead of the
page, and the public site is untouched. Because the door exists, the cards now carry
each app’s own address, including ones withheld while the shelf was open. The
test site on github.io can never have this door, so a staging build strips
every address — if the Access application is ever removed, the addresses come out of
src/data/guides.json in the same change.
Since 2026-09-10 an app can be held off the shelf while it is being rebuilt: the
Amapas 353 website’s guide is written and waiting in that app’s repository,
kept off the shelf at your request because the site changed faster than the guide could
be written. The runbook’s open list says how to bring it back.
Since 2026-09-07 each app has a colour: its name rides a band of it on the shelf
and carries it on the left edge of the side nav, which now lists every app on every
guide page (the open one filled with its own colour, its pages nested under it) so no one
has to scroll the shelf to see what exists. Colours live in
src/data/guides.json; an app without one is given one automatically, and the
suite measures that no two apps share a colour and that each is dark enough to carry the
white name on it.
Since 2026-09-06 the shelf is visually its own place — the owner asked twice,
first for a standalone area, then for one markedly different from the site: no
public-site nav (the App Guides masthead, the back link and each app’s sidebar
are the wayfinding), a cool near-white ground with a teal accent against the site’s
warm cream and amber, the reader’s own system type instead of Archivo, and code
panels highlighted on a light ground. The divergence is asserted by
tests/guides.test.mjs, so re-unifying the two designs by accident turns a
test red.
Since 2026-09-06 the shelf carries its own manual: a Centralized
Documentation card whose page, Adding a new app to the shelf, holds the five
steps end to end — each one a stand-alone “what you do” list, none
needing a terminal — and closes with a prompt the owner pastes into a Claude Code
session on a new app’s repository to have the repository work done for them.
Since 2026-09-05 the sync is armed: the owner created its read token — named
CENTRAL_DOCS_GIT_TOKEN, the owner’s choice, and every page of this
manual now uses that name — so the nightly pass can read the app repositories the
moment any of them has a docs/guides folder. | Each app adds
docs/guides/*.md; from then on the nightly sync brings guides across as
commits the Publish page lists. The door goes up
whenever the owner wants it: the second Access application (allow-list of one, widened
app by app), which locks the shelf without any change to the site. |
| Uploading from the phone | The Shortcut picks its destination from a list — and only from the list
(2026-08-21). The recipe on Uploading from an
iPhone reads /albums.txt from the Worker: every existing album, one per
line, nothing typed. The + New album… row and the typed fallback behind it were
retired on 2026-08-21 — that fork (an If, a second prompt, two tokens to re-point)
was where every failure the recipe had actually lived, and the owner asked for it to go.
A new album's first batch starts on the upload page, which can ask for what a new
album needs; from then on it is a line in the list. Legacy albums are deliberately not
offered: uploading "into" one would land a batch beside a record that cannot hold it. | Typing one is still possible, and now survives being typed badly. A country is
snapped onto the site's roster at both ends of an upload — United States,
united-states and Hong Kong all land in the country that exists
— and a batch already stranded under a wrong spelling relocates itself on import.
Folders that pre-date this show as · not imported on
Gallery cleanup, with an Import button of their own.
Since 2026-09-13 a same-name upload never overwrites by accident: a file that differs
from the one already in the album is filed under the next numbered name, and only an
explicit Replace on the desktop or phone page writes over — the Shortcut, which
cannot ask, can no longer replace anything. The desktop destination list now reads and sorts
Country — year — album name (same date). |
| Photographs from an iCloud link | Switched on since 2026-09-13 — a third way in, beside dragging and the
phone. A Photos Copy iCloud Link pasted on the desktop upload page or the phone page
(/m) makes the pipeline fetch the originals from Apple into the album and import
them; nothing passes through the owner’s machine. Same sizes, same thumbnails, same
checks as any batch. Built the day a runner-side probe proved the originals were reachable
behind a link (Addenda 331–332); the recipe rests on Apple’s undocumented web
client, which is the one thing that can change without notice. Overlap with an album is
decided by rule on the runner, not shown up front; a pre-flight with counts and a
fetch-new / replace-all choice is planned and set aside on the
runbook’s open list (2026-09-13). Every
“started” message on the upload pages now says staging, since that is
where an import lands; Publish is what sends it live (2026-09-13). | Nothing. When a link stops working at the fetch stage, run the probe workflow on it and read its log — the shape that moved will be there. The two zero-code routes (iCloud for Windows to a folder; the link’s own Download button) remain as they were. |
Cutover progress lives on Launch & cutover, where finished steps carry a green Done chip with the date. As of 2026-08-20, steps 1–21 are done (18 and 21 both closed that day) — the domain is on Cloudflare, the new site is what the world sees, mail is routed and DMARC published, the contact form is live, and the admin door is locked (step 21): Access asks for the one-time PIN and let the owner in the same day.
The admin door starts at www.traveller.org/admin/, which lists every tool. It had no page of its own until 2026-08-20 — the tools all live a level down — so that address answered with the site's own 404.
Every row links straight to the console page where that service is actually changed — no hunting through dashboards. Cloudflare links land on the account picker if more than one account is signed in; choose the traveller.org account once and they go direct thereafter.
images.traveller.org.
Not for tidiness — they cannot go anywhere else. There are 46,174 objects in
the bucket (a 2048 px copy and a thumbnail for each of 23,087 photographs, about
14.5 GB). A Cloudflare Pages site on the free plan may contain 20,000 files, so
they could not ship with the site even if the repository were willing to hold them; the
whole production build is 973 files by comparison. They live in R2, which is a separate
service and has to be addressed by some hostname — the only real choice is
between a memorable one and the bucket's default
pub-….r2.dev. Two things follow for free: the bucket can be swapped or
moved without a single URL on the site changing, and images are cached under their own
rules, immutable and forever, independent of how often the pages themselves change.production branch and has been www.traveller.org since
2026-08-19, as well as traveller-org.pages.dev. The
photographs are on Cloudflare R2, and the upload page is a Cloudflare Worker.
InMotion no longer serves the website at all — every visitor now lands on
Cloudflare — but it is not finished: it still holds the 393 redirects for old URLs,
still runs cPanel and webmail on mail.traveller.org, and still forwards
every @traveller.org address to Gmail. That last job moves to
Cloudflare Email Routing the moment the apex MX is freed, and InMotion can be
cancelled after the redirect overlap. Prices below were re-checked against Cloudflare's
published rates in August 2026.| Service | What it does | Cost | Open its console |
|---|---|---|---|
| GitHub | Holds the repository — all content, code, design, and the full history of every change. Editor identity: collaborators on the repo are the people who can publish. | GitHub Pro (private repo + Pages) | Repository · Collaborators · Secrets |
| GitHub Actions | The build robot. Every push runs npm run build (Astro) and deploys the result. Nothing is ever built by hand. | included | All runs · Photo import · Deploy · Worker deploy |
| GitHub Pages | The test server: brarob100.github.io/traveller.org. Serves whatever the last successful build produced. | included | Pages settings · Live test site |
| Cloudflare Pages | The production host since 2026-08-19 — same static output, unlimited bandwidth, 500 builds/month free. Cloudflare put Pages into maintenance mode (confirmed 2026-09-09): new work goes to Workers instead, but Pages still runs and builds with no announced shutdown date, so this site stays where it is — the runbook's Still open list carries what a move would cost and when to make it. Builds from the production branch, which only moves when the Publish button is pressed — staging takes every commit, the live site takes what you approve. | $0 — unlimited bandwidth and requests, 500 builds/month, 5 custom domains per project. The paid tier ($5/mo) buys more builds, which this site will not need. | Workers & Pages |
| Cloudflare R2 | Object storage for full-size images (the photo library outgrows any static host's file limits). Zero egress fees; serves images on the same CDN. | ~$1/yr today (23,087 photographs ≈ 14.5 GB; first 10 GB free, then $0.015/GB-month). Roughly $2/yr per additional 10,000 photographs. | traveller-images bucket · API tokens |
| Decap CMS | The editing interface at /admin (in progress). Saves records straight to the GitHub repo under your name; each save publishes automatically. | $0 | not built yet |
| Cloudflare Access | The door on www.traveller.org/admin*, switched on 19 August 2026 (decided 2026-08-18, no longer optional; set up at runbook step 21, ticked on the 20th): an email allow-list verified by one-time PIN, so only named people ever see the admin pages (including this manual). It does not cover the upload Worker's own workers.dev address — that door is the passphrase, which rate-limits failed guesses. | $0 (≤50 users) | Zero Trust dashboard |
| Friends door (Pages Function) | The shared-password gate on the unlisted albums: friends sign in once at www.traveller.org/friends/ and the unlisted album pages open for a year per device. Runs inside the site’s own Cloudflare Pages Functions (functions/_middleware.js + functions/friends/login.js) — no extra service, no extra cost. The password is the FRIEND_KEY Pages project setting (see the credentials table on the pipeline page); it is deliberately not Cloudflare Access (that door is an email allow-list for you) and not the upload passphrase (that key starts machinery). | $0 | Pages project → Settings → Variables and secrets |
| Guides shelf + Cloudflare Access (second app) | The end-user guides of every app on the domain at www.traveller.org/guides/, built from a copy of each app’s docs/guides/*.md that the nightly Sync app guides workflow commits into this repository (src/data/guides.json lists the apps). Its door is a second Cloudflare Access application on www.traveller.org/guides* with its own allow-list — separate from the admin’s so an app’s users can be let in without being let into the admin. Deferred by the owner on 2026-09-05 — the shelf is live and open (see “Where the domain stands today”). The sync’s read token is CENTRAL_DOCS_GIT_TOKEN (credentials table on the pipeline page). | $0 (Access ≤50 users; Actions minutes negligible) | Zero Trust dashboard · Sync runs |
| Upload Worker | The traveller-upload Cloudflare Worker, reachable at three addresses, all declared in wrangler.toml and all live: upload.traveller.org (a custom domain, since 2026-09-14 — the plain one to type), www.traveller.org/api/ (a route, since 2026-08-20 — the one the admin pages call, because it is same-origin) and its own traveller-upload.brarob.workers.dev (which the iPhone Shortcut carries). See “Where the domain stands today” above. It serves: the passphrase-gated bulk upload page (no file limit, keeps place folders, accepts RAW) and the zip-records API behind the zip records page. Source: cloudflare/upload-worker.js. | $0 | Upload page · Edit code · Secrets |
| Resend | Two jobs: sends the zip-record download link after each photo import (API key = the RESEND_API_KEY GitHub secret), and after cutover is the SMTP relay that lets Gmail send as brobinson@traveller.org and friends (a second API key, typed into Gmail's Send-mail-as settings — cutover step 16). | $0 (100 emails/day free, both jobs combined) | Sent emails · API keys · Domain |
| Anthropic (Claude API) | Drafts the banknote write-ups: when a note scan arrives, the pipeline sends both faces to Claude, which reads the note and writes The artwork / In circulation / Details straight onto the note page; you edit or re-generate it afterwards at Banknote pages. Key = the ANTHROPIC_API_KEY GitHub secret; the model is the ANTHROPIC_MODEL GitHub variable (see the credentials table on the pipeline page). | pennies per note (pay-as-you-go API credit) | Console · API keys · Usage & billing |
| Cloudflare Turnstile | The contact form's CAPTCHA — the "I'm not a robot" check. Privacy-preserving and usually invisible: most visitors are never asked to click anything. Site key is public (in the build — on the Pages project for production and in
deploy.yml for staging), secret key lives on the contact Worker. The widget
renders only on the hostnames listed in its Hostname Management: today
traveller.org, www.traveller.org and
brarob100.github.io. On any other name it draws nothing, which looks exactly
like a broken key. | $0 (unlimited) | Turnstile widgets |
| Contact Worker | The traveller-contact Worker: verifies the CAPTCHA, then emails the message to you through Resend with Reply-To set to the sender, so Reply just works. Deliberately separate from the upload Worker — a public endpoint never shares an isolate with the admin passphrase. Source: cloudflare/contact-worker.js. | $0 | Edit code · Secrets |
| DMARC Worker | The traveller-dmarc Worker (cloudflare/dmarc-worker.js): reads the daily DMARC aggregate reports sent to dmarc@traveller.org — the machine-readable record of everything that claimed to be this domain and whether it proved it — and emails you only when something needs acting on. A clean report produces no mail at all. Built 2026-09-10 so the reports do not have to be read by hand; a by-hand reader for a pile of them is .github/scripts/dmarc_check.py. | $0 | Edit code · Secrets |
| Cloudflare Email Routing | Forwards the eight @traveller.org addresses to Gmail after cutover, replacing InMotion's forwarders. No credentials — destination inboxes verify themselves by clicking a link. The one two-inbox address, theboys@, routes through the traveller-email Worker (cloudflare/email-worker.js, auto-deployed). Set up in cutover step 15 on Launch & cutover. | $0 (unlimited forwards) | Email Routing rules |
| Google Analytics | Visitor statistics for the public pages of the live site, property G-80N129L5BJ, installed 2026-08-26. Excluded on purpose: everything under /admin/, the test site, and the old-URL forwarding pages. The measurement ID is not a secret — it ships in the HTML of every public page, which is how the tag works, so there is nothing to rotate and no credential to store. | $0 | Google Analytics |
| InMotion | The legacy host. No longer serves the website (2026-08-19) — it now only holds the 393 old-URL redirects, cPanel and webmail on mail.traveller.org, and the domain's email forwarders until Email Routing takes over. Retires after that plus a redirect overlap period. | existing plan, then $0 | AMP control panel |
src/data/galleries.json — every gallery: country, year, photo list, and an optional base: "r2" flag marking galleries already upgraded to high-res.src/data/journal.json — all 161 journal entries (title, city, date, country, body, images).src/data/currency.json — 33 countries of banknotes with scans and artwork notes.src/data/places.json — photo→place tags per gallery (Rangoon, Bagan, …). These drive the city-grouped gallery sections and lightbox captions.src/data/home.json — what's featured: carousel slides, home-page journal cards, country card photos.public/thumbs/ — 460 px thumbnails for legacy galleries, deployed with the site. Upgraded galleries serve thumbnails and full-size images from R2; unreplaced full-size images hotlink the legacy server until their batch arrives.src/layouts/ + src/pages/ — the page templates.src/styles/global.css — the whole "Darkroom Daylight" design system: colors, type, every component.design/site-daylight.html — the approved design reference, every page type in one file.Every screen that was designed before it was built lives in the repo as a set of boards, and the boards are what to reopen — by you, or by Claude in a future session — before changing one of those screens or building a new one in the same idiom. They record the approved look and the decisions behind it (annotations on the boards say why a control is where it is), which the built page alone cannot.
design/ios-upload/ — 9 boards: the iPhone uploader at /m
(unlock, album picker and search, new-album, currency, uploading, done and failure
screens). traveller-uploader-iphone.html beside them is the whole set as one
browsable file.design/ios-admin/ — 17 boards: Gallery cleanup on the phone
(Browse / Select / Arrange modes, the sheets, import, delete) and Cards &
strips (hub, carousel, letters home, country covers, the pickers), plus the two flow maps.
traveller-admin-iphone.html is the one-file render.design/iphone-nav/ — 4 boards: the site’s own navigation on
an iPhone in portrait, where the four sections used to straggle across two ragged lines
under the wordmark. The four directions drawn were a centred masthead, a
sideways-scrolling swipe strip, a menu sheet, and an iOS tab bar;
you chose the masthead on 30 August 2026 and it is what the site now does below 700px
— wordmark centred on its own line, a hairline, then the four sections centred
beneath it. The other three boards stay as the record of what was weighed.design/iphone-photo-view/ — 5 boards (2026-09-06; you chose
Option A the same day and it is built — the slim bar, the full-width photograph,
the caption below, the admin verbs behind one chip; the masthead stays, per your
2026-08-30 nav decision, and so do the filmstrip arrows. Option C followed the same
day (“iPhone horizontal should snap to the window”): turning the phone
sideways pins the photograph over the whole window on the board’s dark ground, count
pill fading, rotate back to leave. Board B remains the record of what was weighed, and
its built-in zoom is the natural next step if the appetite returns): the public photo view on an iPhone, drawn because the photograph was
getting about a tenth of the screen and detail needed pinching. Beside the current layout
sit three combinable options — A, the postcard promoted (light page, photo
full-width under one slim bar, admin verbs behind one chip); B, a dark-room viewer
(photo edge-to-edge and centered, chrome overlaid and tap-hidden, pinch or double-tap
zooming into the 3000px large); and C, turn-the-phone (rotating fills the screen
— 2.4× the photo area — with no new chrome).
iphone-photo-view-options.html is the one-file render.design/site-daylight.html (with its editable
-source.html) — the public site, every page type; unlike the boards above it
is kept in sync: any visual change to a public page updates it in the same push.design/design-directions.html and design/home-variations.html
— the exploration that preceded the approved design: the directions considered and
the home-page variants. History, not truth — kept because they show what was rejected.The two ios-* folders are design-canvas projects: each screen
is its own .dc.html artboard (open it in any browser), canvas.json
lays them out, and the single-file render beside them is the shareable copy. The phone
pages were built from these boards screen for screen — where a built page and its board
disagree, the decision log says which one moved and why (Addenda 249–250 for the uploader,
258–260 for the admin pages).
Country stats (galleries, photographs, years, banknotes), region groupings, listing
filters, counts on cards, llms.txt / llms-full.txt, the
sitemap and every page’s structured data are all
derived at build time from the data files. Add a photo and every
number that should change, changes on the next publish.
/admin/docs/) — how to operate the site. Updated in the same push as any change to a process it describes. Golden rule: any service added to or removed from the stack must appear in the table above in the same push.docs/ in the repo — dated decision logs: what was decided, when, and why.CMS.md and REDESIGN.md in the repo — the full CMS plan and the original redesign analysis.www.traveller.org/admin*, this manual included, answers a stranger with the
one-time-PIN login rather than a page. The upload Worker's own workers.dev
address is not covered by it and never can be: its door is the passphrase, which
rate-limits failed guesses.Every page under /admin/ (this manual, the zip records page) shows a different browser-tab icon than the public site — an easy visual cue that you are on an admin page, and the bulk upload page carries the same icon. Source: public/admin-favicon.ico, inlined into the Worker page so it needs no external file.