Repository navigation
feat(provision): provision the SemiPlot pen catalogue (#9) - #11
Merged
Merged
Conversation
mrcsin
marked this pull request as ready for review
September 24, 2026 13:07
SemiPlot is about to let an operator edit a pen, and the database could not hold one: the SemiPlot role could only read, and semiplot_tags had one group name per pen, no stored scale and no stored visibility. The role semiplot_reader becomes semiplot, and its password moves to --plot-password / SEMIBASE_PLOT_PASSWORD. It reads the archive as before and writes its own configuration, and nothing else: a column-level UPDATE on the eight settings columns of semiplot_tags, full DML on semiplot_groups and semiplot_pen_groups, SELECT on semiplot_meta. It never inserts, deletes or re-keys a pen, because a pen's id is the SCADA variable number and belongs to SCADA. semiplot_tags gains unit, format, enabled_on_start and a paired scale_min/scale_max; groups move into semiplot_groups and semiplot_pen_groups, so a pen can sit in several. semiplot_meta records the schema version. New pens come from the keys SCADA writes. semiplot_register_new_pens() adds a hidden pen with default settings for every key in public.trends that has none; the viewer calls it at start and from its editor. It runs as semiplot_registrar, a NOLOGIN role holding only what the body needs, pins search_path to pg_catalog, pg_temp and names its relations by schema, so semiplot cannot make it register a key of its own choosing, and scada_writer, which owns trends, cannot reach superuser through it. What scada_writer can still do as the roles that read its table is stated as the trust model in provisioning.md#trust. The tail check proves the boundary by execution on every run: the allowed writes succeed, the insert, delete and key change are refused, a temporary trends shadow registers nothing, and PUBLIC holds no EXECUTE on the function. CI exercises the function body over real trends rows on PostgreSQL 17 and 14. Claude-Session: https://claude.ai/code/session_01PfZJa7neMMKPzycFR6iuhD
mrcsin
force-pushed
the
semiplot-role-and-pen-schema
branch
from
September 24, 2026 13:13
a80f182 to
15d5871
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
SemiPlot is about to let an operator edit a pen, and the database could not hold one. The SemiPlot role
could only read, and
semiplot_tagscarried one group name per pen, no stored scale and no storedvisibility. Nothing created a pen but a person at a console.
What changed
semiplot_readerbecomessemiplot(--plot-password/SEMIBASE_PLOT_PASSWORD). It reads thearchive and writes its own configuration only: column-level
UPDATEon the eight settings columns ofsemiplot_tags, full DML onsemiplot_groupsandsemiplot_pen_groups,SELECTonsemiplot_meta.It never inserts, deletes or re-keys a pen: a pen's
idis the SCADA variable number.semiplot_tagsgainsunit,format,enabled_on_startand a pairedscale_min/scale_max; groupsmove into their own tables, so a pen can sit in several;
semiplot_metarecords the schema version.semiplot_register_new_pens()adds a hidden default pen for every key inpublic.trendsthat has none.It runs as
semiplot_registrar(NOLOGIN, minimal grants), pinssearch_pathtopg_catalog, pg_tempand names its relations by schema, so
semiplotcannot register a key of its own choosing andscada_writercannot reach superuser through it. The remaining reach ofscada_writer, which ownstrends, is stated as the trust model indocs/architecture/provisioning.md#trust.public.trendsis now created before the SemiPlot schema files, because aLANGUAGE sqlbody ischecked against the tables it names at
CREATE FUNCTION.#8asked for a second role,semiplot_writer. One role with per-table and per-column grants wasbuilt instead: the two passwords would sit in the same
connection.yaml, so a second role separatednothing, and the boundary that matters is enforced by the grants.
How it was verified
go test ./...,go vet,gofmt -l,golangci-lint run: clean.semibase benchtwice on freshpostgres:17-alpineand once onpostgres:14-alpine: every tail checkOK, including the refused insert, delete and key change and the temporary-
trendsshadow replay.trendsshadow no longer registers a key, and a view swapped in byscada_writerruns assemiplot_registrar (super=f)instead ofpostgres (super=t).trendsrows and checks the function returns 3, then 0, with the expecteddefaults.
and the operator walked the SemiPlot demo stand on it.
Closes #9
Closes #8
Per-task commits before collapse
https://claude.ai/code/session_01PfZJa7neMMKPzycFR6iuhD