The directory: publish the first directory relay#
A directory is the phone book of balanced circles: one place where an app finds every circle (name, currency, Circle Heart, witnesses, relays, the newest daily seal, number of members) and every person by key (profile, circle, block joined), and can search by name. It is a public Nostr relay plus a small JSON API. It stores nothing secret and it cannot forge anything, because everything it holds is signed by the people and circles who wrote it. It can only leave something out or be late. The truth about a balance stays in the circle's own ledger (two layers).
The first directory#
| Name | Balance directory 1 |
| Status | pilot (live; it can be emptied and refilled from the circles' relays at any time) |
| Operator | LetsBalance |
| Relay | wss://directory.letsbalance.world/relay |
| API | https://directory.letsbalance.world/v1 |
| Health | https://directory.letsbalance.world/v1/health |
| Read | open: anyone, no key, no login |
| Kinds accepted | 0, 30910, 30915, 30916, 43917, 43918 |
| Registry entry | directories.json, openapi.json |
| On the day of checking (27 Oct 2026) | 1 verified circle (Mejmo Se Fajn), 1 member; no daily seal yet |
Nothing about circles is sent to the Lana relays. Those stay for Lana only; this directory is the new place (decision E-14, 26 Oct 2026).
What it accepts and what it refuses#
The relay runs a write policy in "directory" mode. It is strict on purpose.
| Kind | What | Accepted when |
|---|---|---|
| 30910 | circle record (d = circle id) | version 3; signed by the circle key it names; the founder's BIP-340 signature g verifies; the circle is admitted and the record's first block, circle key, founder, witnesses, quorum and mirror_of are those of a mirror of its verified chain; an older mirror_of than the held one is refused |
| 30915 | circle head (the newest seal's mirror, d = circle id) | the relay holds the circle record; signed by a current witness; a valid quorum of qs; the record's circle key; it extends the head held, by prev |
| 30916 | member home (d = letsbalance, c = circle id) | attested by a witness who was in office at the join height; g = the circle's first block |
| 43917 | day seal | as a head; made on its own day (created_at within 2 days of day); only the newest 7 days per circle; never two seals for one day |
| 43918 | snapshot chunk | from a current witness, for a seal day the relay holds; its count equals the seal's snap count; one chunk per index |
| 0 | profile | anyone, with limits: content a JSON object of at most 16 KB, at most 64 tags, no expiration or - tag, 10 per author and 600 per sender address an hour, at most 100 with one and the same created_at |
| anything else | refused: blocked: this relay is the Balance directory: it keeps circle records (30910), heads (30915), member homes (30916), the newest day seals (43917) and snapshot chunks (43918) of the circles it lists, and profiles (KIND 0); the book of a circle lives on the circle's own relays | |
| KIND 5, KIND 62 | deletion, vanish | refused: blocked: the Balance directory never deletes |
A circle is listed only if its chain was verified. The directory's collector reads the relay of every circle the operator names, verifies the chain from block 1 (signatures, witnesses, the first block), and writes the circle's facts (first block, circle key, founder, witnesses, quorum, mirror_of) into an admission file that the relay reads. The relay accepts a circle record only if it matches. So nobody can list an invented circle, take a birth number before its founder signs, or replace a circle's key and witnesses with a higher mirror_of. Publishing a record does not list a circle.
Old seals and chunks (older than 9 days) are removed hourly: a directory is the present, not an archive. Nothing else is deleted on request.
How to read it#
- API:
/v1/circles,/v1/circles/{birth},/v1/people/{hex},/v1/search?name=,/v1/health. Every route, parameter and real answer is in the AI guide, section 2 and in openapi.json. - Relay:
["REQ","c",{"kinds":[30910]}]and the other filters in the AI guide. At most 500 events per REQ: page withuntilinclusive and de-duplicate by id. - A person before a circle:
GET /v1/people/{hex}finds a person who only has a profile: when the directory holds none it reads the key directly (its own relay, then the Lana relays, read only) and answersinCircle: false,profileSource: "lana-relays". Nothing is stored by that. Search by name needs the profile on the directory relay: an app publishes the person's own signed KIND 0 there. - Scripts:
read-directory.mjs.
How to run your own directory#
Anyone can run a directory. It is two containers and one shared volume. This is a summary of the reference implementation's operator notes; the implementation is in the project's repository (a standalone public package is not yet published; ask the project).
- The relay container. A strfry relay (v1.1.0, the same as a circle's relay) with the write policy in mode
directory(a Python scriptledger-write-policy.py, standard library only). It reads the admission file from the shared volume. An hourly job removes seals and chunks older than N + 2 days (N = 7 by default). - The collector and API container. The collector reads, every minute, the relay of each circle in the operator's list (
DIRECTORY_CIRCLES), verifies each chain from block 1, writes the admission file, and sends to the relay only what holds. For every member it reads the profile from other relays, read only, and copies it if it is newer (at most 500 an hour). Profiles carrying an email or a phone number are not copied by default. The read API answers/v1/...and returns 503 until the collector has finished its first round. - Settings are a small
.env: your domain, the circles you list, the number of days of present state, a dry-run switch. Start in dry run (the collector prints what it would store and sends nothing), look, then switch storing on. - DNS and HTTPS in front, as for any site. The relay speaks
wss://<domain>/relay, the APIhttps://<domain>/v1. - Reset is safe: remove the relay's volume and start; the collector refills circle records, heads, seals, snapshots and homes from the circles' relays within minutes. Only profiles that people published themselves are lost (they can publish again).
- Register it: the registry directories.json lists known directories. How a new directory gets listed is not yet specified.
Never run docker volume prune or docker system prune on a host that holds a directory or a circle.
The many-directories idea, and what is built#
The idea: many equal directories (the target is as many as people want, up to about a thousand), each holding the same index and the present state of all circles, run by different people. An app asks several and takes the union. Because a directory cannot forge anything, a bad or slow one can only fail to show something; others show it.
| Part | Status |
|---|---|
| One directory relay with the write policy, the collector and the JSON API | Built and live (pilot; running when checked on 27 Oct 2026) |
| Verification of each circle's chain from block 1 before listing; the admission file | Built |
| The operator names the circles that are listed | Built (a manual step; a hand-picked list) |
| A person is found by key even before joining a circle (read-only fallback to the Lana relays) | Built |
| Circles send their records to the directory instead of the Lana relays | Prepared (a switch per kind, off by default; each kind goes only after an explicit decision) |
| Several directories that share data with each other (NIP-77 style sync) and watch the circles' relays by themselves | Planned |
| A signed daily summary from every directory ("my list of circles and their heads") so directories can be compared | Planned |
| A circle applies to a few directories with its founding charter; the directory admits it when its chain checks out (instead of the operator naming it) | Planned (proposal) |
| Archives: the full history of each circle, in 5 archives chosen by lot from the circle's number; older than a year on cold storage | Planned |
| Vaults for the Compliance Passport (encrypted, deletable, separate) | Planned, see BalanceID |
| Protection of KIND 0 from junk (proof of work NIP-13, an invitation, or a badge) | Open |
Rough sizes from the plan of 26 Oct 2026 (an assumption of 30,000 circles, 500 members each, 2 payments a day; estimates, not measurements): the person index of 15 million people about 20 GB per copy; the present state of all circles about 3 GB, replaced every day; the whole history about 80 TB a year (40 TB compressed), which is why the history belongs in archives and not in directories.
Open points of the first directory#
- Profiles from anyone are accepted, with limits. Even if someone floods the relay, circles, members and profiles of members are read separately and the answer says
incompleteKinds. - How a new circle enters: today the operator names it. A circle cannot enter by itself. This closed squatting on birth numbers and invented circles. Whether that fits "no central authorities" is answered by there being many directories and by the circle's ledger staying the only truth.
- Contact data: profiles with an email or phone number are not copied by default; the API shows only 8 profile fields, but the relay serves the whole raw event to anyone.
- Sender address: the relay cannot trust the forwarded-for header, so the collector itself stays under the per-address limit.
- Size: the collector re-reads whole chains every hour and the API keeps all profiles in memory. Fine for hundreds of people; thousands of circles will need reading from the last seal onward, and a database.