Central platform for European atproto.<cc> country community websites atproto.eu
community atproto
README.md

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 at https://bsky.social/xrpc/com.atproto.identity.resolveHandle?handle=<handle>.)
  • Show your own events → nothing to do: any atmo.rsvp event on your social.did appears automatically. To also pull a city chapter, add { did, handle } to eventDids.
  • 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: true and list the verifier account(s) in verifiers. 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 in memberSeed.
  • Stats are the country's. There are no city-level population numbers, so the hero figure and /community-size show the surrounding country's estimate, labelled with the country ("in Spain").
  • The map shows your region, not a blob. Set mapBox to frame it (Barcelona → Catalunya); the country still colours the land. (A landmark-silhouette treatment is being explored — see brand/kit/barcelona/LANDMARK-MOCKUPS.md in 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.