The root AGENTS.md holds what applies to every app and package (how work is done, Serverpod, checks with Melos and the tools, CI); Learn's own is apps/learn/AGENTS.md, Todo's a note that it is still the scaffold. The root README introduces the project, Learn's moves to its folder; LICENSE.md says which licence covers which directory, Todo's packages unlicensed until decided; one CONTRIBUTING.md. ADR 0015 records the decision; ADR 0014 and 0003 note what moved. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
110 lines
5.4 KiB
Markdown
110 lines
5.4 KiB
Markdown
# halkyon_base
|
|
|
|
What every Halkyon server has, as a Serverpod module (`halkyon_base_server`, with its
|
|
generated client `halkyon_base_client`): signing in, the account's ways in and its
|
|
deletion, the pruning of what nobody can return to, and what an instance tells its apps
|
|
before anyone signs in. It comes from Halkyon Learn's server, whose rules it keeps.
|
|
|
|
## What it provides
|
|
|
|
Endpoints, under the nickname `base` in a host's client (`client.base.emailCode`):
|
|
|
|
| Endpoint | Methods | Who may call |
|
|
|---|---|---|
|
|
| `anonymousIdp` | `login` (Serverpod's anonymous sign-in, at most 30 new accounts per IP address and hour) | anyone |
|
|
| `gitHubUpgrade` | `upgrade`: links a GitHub account to the caller, who becomes `identified` | signed in |
|
|
| `gitHubSignIn` | `signIn`: a session for whoever has that GitHub account | without a session |
|
|
| `emailCode` | `sendCode`, `verifyCode`: a code by mail; a new address becomes the caller's, another person's gives a switch token | signed in |
|
|
| `emailSignIn` | `signIn`: a session for the person a switch token names | without a session |
|
|
| `account` | `delete`, `signInWays` | signed in |
|
|
| `instance` | `basics`: the app it serves, GitHub's client ID if any, whether email sign-in is on, the source link, the oldest app build, where data and backups are | anyone |
|
|
|
|
- GitHub keeps only the numeric GitHub ID, in Serverpod's own table; email keeps only
|
|
HMAC-SHA256 of the address, keyed with `emailSignInPepper` (`email_identity`), and its
|
|
codes as Argon2id hashes (`email_code_request`). Codes are eight Crockford characters,
|
|
valid 15 minutes, 3 tries; at most 3 an hour per address and 10 per network.
|
|
- Refusals are `GitHubSignInRefusedException` and `EmailSignInRefusedException`, each with a
|
|
reason.
|
|
- For the host's own code, `package:halkyon_base_server/base.dart`: the configuration,
|
|
`tokenManagerBuilders` and `identityProviderBuilders`, the `identified` scope and
|
|
`grantIdentified`, `deletePerson`, the pruning (`pruneAbandonedAnonymousPeople`,
|
|
`forgetOldSignUpAttempts`, `forgetExpiredEmailCodes`), short codes, `sourceCodeUrl`,
|
|
`dataLocation`, `backupLocation`, and the models.
|
|
|
|
## Taking it into a host
|
|
|
|
1. Depend on both packages by path, within the repository's workspace (an app's
|
|
packages sit in `apps/<app>/`):
|
|
|
|
```yaml
|
|
# the server's pubspec.yaml
|
|
halkyon_base_server:
|
|
path: ../../../packages/halkyon_base_server
|
|
# the client's pubspec.yaml: the same with halkyon_base_client
|
|
```
|
|
|
|
2. Name it in the server's `config/generator.yaml`:
|
|
|
|
```yaml
|
|
modules:
|
|
halkyon_base:
|
|
nickname: base
|
|
```
|
|
|
|
3. Configure it once, before the pod starts, and run its authentication and pruning:
|
|
|
|
```dart
|
|
final pod = Serverpod(args); // the host's generated Serverpod
|
|
HalkyonBase.configure(
|
|
pod,
|
|
HalkyonBase(
|
|
app: 'todo', // its apps accept only instances naming it; never change it
|
|
appName: 'Halkyon Todo', // in sign-in mails
|
|
upstreamSourceCodeUrl: 'https://code.halkyon.app/project-halkyon/halkyon_todo',
|
|
minimumAppBuild: 1, // raise it in the commit that breaks older apps
|
|
deletePersonData: [deleteTodosOf], // the host's own data of a person
|
|
),
|
|
);
|
|
pod.initializeAuthServices(
|
|
tokenManagerBuilders: tokenManagerBuilders,
|
|
identityProviderBuilders: identityProviderBuilders(pod),
|
|
);
|
|
```
|
|
|
|
Then schedule the pruning in a future call of the host's own: daily
|
|
`pruneAbandonedAnonymousPeople`, hourly `forgetOldSignUpAttempts` and
|
|
`forgetExpiredEmailCodes`.
|
|
|
|
4. A host's deletion (`PersonDataDeletion`) runs inside `deletePerson`'s transaction,
|
|
after the person's sessions ended and before their identities and the person go, in
|
|
the order given; it deletes the person's rows in the host's tables, and returns what
|
|
has to wait until the deletion is committed (files outside the database), or null.
|
|
Pruning deletes through the same function.
|
|
|
|
5. Create the host's migration. A host taking the tables over from its own models (as
|
|
Halkyon Learn does) finds no change once its copies are gone: Serverpod matches tables
|
|
by name, and these are identical. `create-migration --empty` then records the module's
|
|
version, with no SQL besides.
|
|
|
|
The passwords it reads: `serverSideSessionKeyHashPepper`; for GitHub `githubClientId` and
|
|
`githubClientSecret`; for email `emailSignInPepper` and `emailSmtpHost`,
|
|
`emailSmtpPort`, `emailSmtpUser`, `emailSmtpPassword`, `emailFrom`. Never change either
|
|
pepper once people sign in: every session or address hash would be lost. The environment:
|
|
`SOURCE_URL`, `DATA_LOCATION`, `BACKUP_LOCATION`: where the instance keeps people's data
|
|
and its backups, each `EU`, a country's code (`DE`) or a subdivision's (ISO 3166-2,
|
|
`US-CA`, `DE-BY`), the backups also `none`; where the disks are, not which law governs.
|
|
|
|
## Developing it
|
|
|
|
`serverpod --no-analytics generate` here after changing a model or an endpoint, and
|
|
`serverpod --no-analytics create-migration` after changing a table. Tests need no Docker:
|
|
`dart test` here starts Serverpod's embedded PostgreSQL. They read `config/passwords.yaml`
|
|
(git-ignored) with throwaway values under `test:` for `database`, `serviceSecret`,
|
|
`serverSideSessionKeyHashPepper` and `emailSignInPepper`.
|
|
|
|
## Licences
|
|
|
|
The server package is under the Server Side Public License 1.0, as the Halkyon servers
|
|
are; the client package under the GNU Affero General Public License 3.0 or later, as the
|
|
Halkyon apps are. So each combines with its counterpart in a host exactly as the host's
|
|
own do.
|