Community config — the community-manager control panel #
Everything a local community manager tunes for their community lives in one file: scopes.ts.
Each community is one entry in SCOPES, keyed by its code — usually a country's two-letter code
(nl, be, pt, …), or a city name for a city community (barcelona). Change a value, open a
pull request, and the next deploy rebuilds that community's site.
Every field below is documented inline in scopes.ts too (hover the field). This README is the
friendly overview. Most fields are optional with a sensible default, so a new community only
writes what differs from the norm.
The knobs #
| Field | What it does | Default if you omit it |
|---|---|---|
social |
Your community's account: { did, handle }. The DID is the real identity (survives handle renames); the handle is display only. Drives "Say hi", the footer follow, and is automatically an events source. |
required |
locales |
The languages your site is built in, in order. The first is the default shown at /; others live under /<lang> (e.g. ['es','en'] → Spanish at /, English at /en). |
required |
verify |
true shows the verifier page, homepage teaser and footer link. Set true only if you actually verify community members. |
required |
verifiers |
If verification is delegated, the verifier account(s) — { did, handle, url, remit } each. A scope can have several (Spain has two, covering different groups); the /verify page lists them. Only meaningful with verify: true. |
[] (you verify yourself, or nobody does) |
managers |
The people who run this site, as DIDs. Shown as the site's managers and listed as members (deduped). Empty = the "help run this" invitation shows. | [] |
memberSeed |
A manual starter list of members, as DIDs — people you vouch are part of the community before anyone signs in. Deduped with managers and everyone who signs in. | [] (the list still fills from sign-ins) |
memberListUri |
A public Bluesky list to use as the member roster instead of the seed. | null |
eventDids |
Extra atmo.rsvp accounts to pull events from, as { did, handle } — beyond your own social.did, which is always included automatically. Use for sibling city chapters. |
[] |
feed |
Your homepage feed: { rkey } of the custom feed generator (under gui.do's account). |
null (no homepage feed) |
nav |
Hide optional menu items: { members, stats, events, tools, builders }, each true/false. About/home always show; Verify follows verify. |
all shown |
accent / accent2 |
Your flag's two wordmark colours (pack names from accents.ts). |
required |
cities |
Cities to plot on the map, Name: [lon, lat]. |
required |
endonyms |
Each locale's name in its own language, for the switcher. | required |
tier |
'country' (a national site) or 'city' (a single-city community, e.g. Barcelona). A city behaves differently — see City communities below. |
'country' |
mapBox |
City only: [west, south, east, north] (lon/lat) to zoom the map to your region while the surrounding country colours the land. |
auto-fit the country |
Common tasks #
- Add a manager → add their DID to
managers. (Resolve a handle to a DID athttps://bsky.social/xrpc/com.atproto.identity.resolveHandle?handle=<handle>.) - Show your own events → nothing to do: any atmo.rsvp event on your
social.didappears automatically. To also pull a city chapter, add{ did, handle }toeventDids. - Change the default language → put that locale first in
locales. - Hide an empty section (e.g. no tools yet) →
nav: { tools: false }. - Turn on the homepage feed → set
feed: { rkey: 'atproto-<cc>' }once the feed is published. - Delegate verification → set
verify: trueand list the verifier account(s) inverifiers. One entry renders a single card; several render a "pick your verifier" list (see Spain, which has two — one for Spanish speakers worldwide, one for accounts based in Spain).
City communities (tier: 'city') #
A city (e.g. barcelona) is not a country and never stands in for one. It sits inside a country
(via countries: ['es']) for context, but behaves differently on purpose:
- Members are curated. A city shows only
managers+memberSeed+ its own sign-ins, never everyone in the surrounding country (membership records are tagged by country, not city). So keep your roster inmemberSeed. - Stats are the country's. There are no city-level population numbers, so the hero figure and
/community-sizeshow the surrounding country's estimate, labelled with the country ("in Spain"). - The map shows your region, not a blob. Set
mapBoxto frame it (Barcelona → Catalunya); the country still colours the land. (A landmark-silhouette treatment is being explored — seebrand/kit/barcelona/LANDMARK-MOCKUPS.mdin the workspace.) - It's kept out of the country network. A city is excluded from the
.eu"Country communities" strip and the footer's national list; it appears in the city-communities directory instead. - Verifiers can be shared. A city can reuse its country's verifiers (Barcelona reuses Spain's).
Two things a city also needs in the i18n (src/i18n/), not scopes.ts: a locative in IN_COUNTRY
(e.g. barcelona: { en: 'in Barcelona', ca: 'a Barcelona' }) and, if you want the stats labelled
differently, stats.estimate / stats.count overrides in that scope's uiByScope block.
Rules that keep us safe #
- Always DIDs, never handles for identity (
managers,memberSeed,eventDids[].did,verifiers[].did,social.did). Handles can be re-registered; DIDs can't. The tests enforce this. - Don't point anything at a
*.atproto.<cc>handle as an identity key — those domains are borrowed.
Background and rationale: decisions/2026-09-21-centralized-country-config.md in the workspace.