You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+8Lines changed: 8 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -123,6 +123,14 @@ Shard Core logs in to that registry at startup, using the credentials checked in
123
123
124
124
If you would rather not depend on our infrastructure, note that this is what you would have to replace.
125
125
126
+
### The owner's email address
127
+
128
+
The owner's address lives on their user row and is verified by definition: a new address is only a candidate until somebody opens a confirmation link that was delivered to it. On a hosted shard the controller does that delivery, and it is also what mails the owner about disk space, billing and service notices.
129
+
130
+
A self-hosted shard has no controller and therefore no way to send mail at all, so it cannot run that round trip. Set `email.enabled = false` in `local_config.toml` and the address is taken as given, with no candidate step and no confirmation mail — acceptable because you control the machine. Leave it at its default of `true` and setting an address fails with HTTP 502 — the address is kept as a candidate, but the confirmation mail that would promote it cannot be delivered.
131
+
132
+
The address is what the built-in OIDC provider emits as the `email` claim, alongside `email_verified`. With no confirmed address it emits neither, and apps you log into through it will create fresh accounts rather than linking to an existing one by address.
133
+
126
134
### Localhost
127
135
128
136
In order to test freeshard, you might want to launch it on localhost first.
Postgres data is not part of the rclone backup set (which only syncs `core/`/`user_data/`). To keep it, `database/db_snapshot.py` dumps all application tables to `core/db_snapshot.json` before each backup, and `init_database()` restores it on a fresh shard (before the default identity is generated, so the restored identity survives). Pre-0.38 backups are restored from TinyDB by `tinydb_migration.py` instead.
77
80
@@ -90,6 +93,14 @@ Started at app lifespan startup, stopped at shutdown:
`users.email` is the single home for a person's address and is **verified by definition**; `users.pending_email` is an unverified candidate, at most one in flight per user. That invariant is the only reason the OIDC provider may assert `email_verified: true` — with no verified address it emits neither claim, and there is deliberately no synthetic fallback.
98
+
99
+
-`service/owner_email.py` owns every transition. Anything that writes `pending_email` must retire the token with it, or the token promotes an address it was never sent to.
100
+
- On confirmation, in this order: notify the old address, promote, mirror to `shards.owner_email`, notify the new one. The first step must precede the mirror — the controller's relay only ever reaches the address it currently has on file.
101
+
-`POST /public/users/confirm-email` is unauthenticated by design and the token is its only credential. There is no `GET`: mail scanners and link prefetchers would burn a single-use token.
102
+
-`email.enabled` (default `true`) says whether the shard can send mail at all, and is the explicit self-hosted signal — no controller means no mail, so the address is set directly. Never infer it from a failed delivery; a controller outage would silently downgrade a security control.
103
+
93
104
### Signals (Event System)
94
105
Blinker-based async signals defined in `util/signals.py`. DB-writing handlers are async and called via `await signal.send_async()`:
0 commit comments