# 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`: 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, pinned to a signed commit of `halkyon_shared`: ```yaml # the server's pubspec.yaml halkyon_base_server: git: url: https://code.halkyon.app/project-halkyon/halkyon_shared.git path: packages/halkyon_base_server ref: # 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( 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`. ## 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.