b0a02c010b
Closes #5. Three of the server's tunables were environment variables, which meant changing how long an incident waits before being paged again required editing a chart, merging it and waiting for a reconcile. They are behaviour rather than infrastructure, and the difference is who needs to change them and how often. The split is by who owns the value. What stays in the environment is where the server is plugged in: the listen address, the DSN, the ntfy URL and token, the public URL. Those are needed before the database is open and two of them are credentials -- the settings endpoint reports that ntfy is configured and that a token is set, and never what either is. What moves is how it behaves: the notify repeat interval, the stale window and the archive window. The environment variable becomes the seed rather than the setting, written once on first start and never overwritten, so a redeploy cannot put a chart's default back over an administrator's edit -- the rule the per-team dead man's switches already follow. The loops read the current value per tick, so a change at 02:00 is obeyed at 02:00. Key/value rather than a column per knob: #6 and #7 will both add settings, and a table shaped one-column-per-setting needs a migration for each. The cost is that values are text and the accessor has to say what type it wanted, which settings.go does in one place. Unknown keys are refused rather than stored -- a typo that wrote notify_repeat_second would otherwise sit in the table looking like configuration and doing nothing -- and each value has bounds loose enough to catch a slipped decimal point without having an opinion about anybody's rota. Disabling an account is new, and is not deleting one. Deleting a user nulls acknowledged_by and assigned_to, which quietly rewrites who did what during an incident months after the fact. A disabled user cannot authenticate by either credential, loses their sessions immediately, and stays the name on every acknowledgement they made. The check is part of the lookup in serveAs rather than a test afterwards, so there is no path where the row is loaded and the flag is then forgotten. The page itself is a fourth tab, shown only to an administrator and only as a courtesy: every endpoint under it is refused with 403 regardless, so somebody who types /admin gets an explanation rather than a blank screen. It lists teams with their size and open-incident count, users with their flags, and the settings with their bounds -- plus the environment half, read-only, so somebody hunting for the ntfy URL learns where it lives instead of concluding the server has none. Delete is disabled rather than offered-and-refused for a team with open incidents, and neither admin action is offered on your own account, since the server refuses both. Claude-Session: https://claude.ai/code/session_01RHPj4ggeFdEjKKfm4SHbD7
36 lines
1.9 KiB
SQL
36 lines
1.9 KiB
SQL
-- Settings that an administrator can change without a redeploy, and the flag
|
|
-- that takes an account out of use without deleting it.
|
|
--
|
|
-- Three of the server's tunables were environment variables, which meant
|
|
-- changing how long an incident waits before it is paged again required editing
|
|
-- a chart, merging it, and waiting for a reconcile. They are behaviour, not
|
|
-- infrastructure, and the difference is who needs to change them and how often.
|
|
--
|
|
-- What stays in the environment: the ntfy URL and token, the database DSN, the
|
|
-- listen address and the public URL. Those are where the server is plugged in
|
|
-- rather than how it behaves, they are needed before the database is open, and
|
|
-- two of them are credentials.
|
|
--
|
|
-- Key/value rather than a column per setting. A settings table with one row and
|
|
-- a column per knob needs a migration for every new knob, and #6 and #7 will
|
|
-- both add some. The cost is that values are text and the accessor has to say
|
|
-- what type it wanted; settings.go does that in one place.
|
|
--
|
|
-- No rows are seeded here: a migration cannot read the environment. The server
|
|
-- inserts each key from its own configuration at startup, once, so an install
|
|
-- that upgrades keeps exactly the behaviour it had. See SeedSettings.
|
|
CREATE TABLE settings (
|
|
key TEXT PRIMARY KEY,
|
|
value TEXT NOT NULL,
|
|
updated_at BIGINT NOT NULL DEFAULT FLOOR(EXTRACT(EPOCH FROM now()))::bigint
|
|
);
|
|
|
|
-- Disabling an account rather than deleting it: the person has left, or the
|
|
-- credential is suspect, and their incidents, acknowledgements and timeline
|
|
-- entries must stay exactly where they are. Deleting a user nulls their
|
|
-- acknowledged_by and assigned_to, which quietly rewrites history.
|
|
--
|
|
-- A disabled user cannot sign in and their API keys stop working, but they are
|
|
-- still a name the timeline can show and still a member of their teams.
|
|
ALTER TABLE users ADD COLUMN disabled_at BIGINT;
|