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
+35-2Lines changed: 35 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -18,9 +18,20 @@ cp .env.example .env # fill in DATABASE_URL, JWT_SECRET, CORS_ORIGINS
18
18
go run .
19
19
```
20
20
21
-
Run the migrations in `migrations/` against your database first, in order (copies of CrydenSync's own migrations, kept here so this repo is self-contained for local dev and CI — same as `typebook` keeps its own copy). `002_oauth_identities` is required even if you don't use OAuth yet — `NewOAuthStore` is wired into the engine config unconditionally. `004` through `008` are the TOTP, WebAuthn, recovery-code, login-attempt and API-key tables; run them even if you leave `ENCRYPTION_KEY` unset, since `007` is what the engine's credential-stuffing detection reads once Tier 2 wires it up and `008` is what the API-key work will use.
21
+
That is the whole setup. Migrations are applied automatically on boot — see [Migrations](#migrations) — so there is no separate step to run and nothing to remember after a `git pull`. Or with Docker, which needs no Go toolchain at all:
22
22
23
-
That paragraph is the Postgres path only. On SQLite there is nothing to run by hand — `main.go` calls cryden's own `sqlite.Migrate` at startup. See [The two backends](#the-two-backends).
23
+
```bash
24
+
docker run --env-file .env -p 8080:8080 ghcr.io/crydensync/api:latest
25
+
```
26
+
27
+
A SQLite deployment can skip the database server entirely by setting `SQLITE_PATH` and mounting a volume for it:
28
+
29
+
```bash
30
+
docker run --env-file .env -p 8080:8080 -v api-data:/data \
On SQLite, note that the container runs as a non-root user (uid 10001); a host directory mounted into `/data` must be writable by it.
24
35
25
36
OAuth is optional. To enable a provider, set its client ID/secret plus `BASE_URL` (used to build the callback URL registered in that provider's console):
26
37
@@ -90,6 +101,28 @@ The connection is opened with three pragmas, all of them load-bearing: `foreign_
90
101
91
102
A clean stop is what closes that window: on `SIGTERM` or `SIGINT` the server stops accepting connections, waits up to 30 seconds for the requests already in flight, stops the webhook worker and digest scheduler, and closes the database — which on SQLite is the checkpoint that folds the `-wal` file back into `api.db` and removes the sidecar files. So `systemctl stop`, `docker stop` and Ctrl-C all leave a `api.db` that is complete on its own. A `kill -9`, a crash or a power loss does not, which is why the paragraph above still stands.
92
103
104
+
## Migrations
105
+
106
+
There is no migrate step in the setup instructions because there does not need to be one. Migrations are compiled into the binary and applied on boot, so a `git pull` or a new container image brings its own schema change with it.
107
+
108
+
The two backends are migrated by different code, and that is deliberate rather than an inconsistency:
109
+
110
+
-**Postgres** — this repo's own runner (`migrate.go`) over this repo's own `migrations/*.sql`, embedding all fourteen into the binary. It records what it applied in a `schema_migrations` table it creates itself, so a second boot is a no-op.
111
+
-**SQLite** — cryden's runner over cryden's embedded migrations, because cryden owns that schema. See [The two backends](#the-two-backends).
112
+
113
+
Both follow the same rules: `NNN_*.up.sql` in filename order, one transaction per file, and `.down.sql` files are never run automatically — an automatic rollback of a schema holding live credentials is not something a boot path should be able to do by accident.
114
+
115
+
```
116
+
api migrate # apply pending migrations and exit; starts no server
117
+
api migrate --baseline # record every embedded migration as already applied
118
+
```
119
+
120
+
`api migrate` is for teams who would rather schema changes be a reviewed step than something that happens during a rolling deploy. Set `SKIP_AUTO_MIGRATE=true` and the server starts without touching the schema; `api migrate` is then the step, in your pipeline, before the new version goes out.
121
+
122
+
**If your database already has this schema but no `schema_migrations` table** — you applied the SQL by hand, or an earlier version of this repo had CI do it for you via `psql` — then run `api migrate --baseline` once before your first start. Without it, auto-migration starts at `001` and stops on `relation "users" already exists`: a deployment that cannot start, caused by the feature meant to make starting easier. Baseline records every embedded migration as applied and runs none of them.
123
+
124
+
It is an explicit command rather than something the boot path detects, because the alternative is guessing. "The tracking table is missing but `users` exists, so assume everything ran" is right for the case above and silently wrong for a database that is genuinely half-migrated — it would mark unrun migrations as applied, and the next deploy would look for columns that were never created.
125
+
93
126
## Second factors
94
127
95
128
TOTP and passkeys are optional and all-or-nothing on `ENCRYPTION_KEY`: cryden refuses to construct an engine with a TOTP or WebAuthn store set and no encryption key (a TOTP secret must be recoverable in plaintext to check a code, so it is encrypted rather than hashed). With the key unset, both methods answer `404 totp_not_configured` / `404 passkeys_not_configured` per request, the same shape an unconfigured OAuth provider uses, rather than the server refusing to start.
Copy file name to clipboardExpand all lines: config/config.go
+15-1Lines changed: 15 additions & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -18,6 +18,7 @@ type Config struct {
18
18
// exclusive — Load refuses both or neither. See UsesSQLite.
19
19
DatabaseURLstring
20
20
SQLitePathstring
21
+
SkipAutoMigratebool
21
22
JWTSecretstring
22
23
Portstring
23
24
CORSOrigins []string
@@ -351,6 +352,7 @@ func Load() (Config, error) {
351
352
ifcfg.DatabaseURL!=""&&cfg.SQLitePath!="" {
352
353
returncfg, fmt.Errorf("DATABASE_URL and SQLITE_PATH are mutually exclusive — set one; the admin console's tables are Postgres-only, so a SQLite deployment serves core auth and nothing under /v1/admin")
0 commit comments