Releases: klinux/dbportal
Release list
v0.9.1
The Vault token is renewed from boot
Every datasource with a Vault credential failed at once after a quiet period: the deployment's periodic token had expired. Renewal ran only inside a Vault read, and a read happens only when such a datasource is resolved past the five-minute credential cache, so a 24-hour period could pass with nobody asking (docs/CONTEXT.md §4.5).
- The token is now renewed in the background from boot, in the studio, the worker and the agent alike: a one-minute tick, each renewing only when half the lease has passed. The token lives as long as any pod does.
- The first tick runs at boot, so an already-expired token is a warning (
Vault token renewal failed, status 403) in the process's first minute rather than the first failed datasource. - A 403 at renewal is asked again in a minute instead of silencing the renewal for the life of the process; a 400 (a token Vault does not renew) is still asked once.
Recovery for a token that already expired: create a new periodic token (vault token create -policy=dbportal -period=24h -orphan), store it as vault_token, sync.
Image: ghcr.io/klinux/dbportal:0.9.1 · chart 0.1.35.
v0.9.0
The bot holds its own request, and the thread decides
Two additions to the bot API, POST /api/v1/executions (docs/CONTEXT.md §4.57):
review: { reason }holds the request for a reviewer whatever the datasource's policy would have let run, like a guardrail the bot raised itself. The reason (trimmed, at most 500 characters) is stored on the record, shown as a badge on the reviewers' page, in the Slack announcement and in whatGET /api/v1/executions/[id]returns.400for areviewwithout a reason.- The announcement goes into the request's thread. The "waiting for approval" message with its Approve/Reject buttons is posted into the thread the request named (
reply) as well as toSLACK_APPROVALS_CHANNEL, so the team that watches the thread decides where the request was made. Either copy settles the request; the other answers "already decided" when pressed later. A deployment with no reviewers' channel still announces in the thread.
Nothing else moved: who may decide is still the datasource's approverRoles (Slack users through a named role with user:slack:<id> members), two reviewers still means two, and the outcome still lands in the thread.
Image: ghcr.io/klinux/dbportal:0.9.0 · chart 0.1.34.
v0.8.1
Monitoring follows the object rules
A statement somebody else ran was the one place a hidden name could still appear: the monitoring panel listed every table's statistics and every slow query and active session to anyone who could open the datasource.
- The scoped provider now covers the five monitoring reads: a table's and an index's statistics by the table's address, and a slow query or an active session by the statement it carries, kept only when the same scanner the execution gate uses finds it names nothing hidden, and dropped when it cannot be read at all.
/api/db/monitoringand the agent's operations readings go through it. An engine with no containers reports an empty schema, so the address there is the name alone.
See docs/CONTEXT.md §4.56.
Image: ghcr.io/klinux/dbportal:0.8.1 · chart 0.1.33.
v0.8.0
Which objects each person sees, decided on the datasource
A datasource may now carry objects: rules of match (a name pattern) and roles (the same principal vocabulary as roles). With no rule nothing changes. With any, a person sees exactly the objects some rule they hold matches, and may name only those in a statement. An administrator always sees everything.
- Patterns. Matched against the object's dotted path (
public.orders,apim-2026.*) with*for any run and?for one character, case-insensitively; a pattern with no dot matches the relation's own name in any schema, the natural spelling on a search cluster. A matched object covers what nests under it. - Visibility. Applied on the provider every object route reads through: the tree, the counts, the autocomplete, the schema diff and the agent's grounding show the same thing. A hidden object's detail is "not found", and the rules are never sent to the browser.
- Enforcement. Every statement on
/api/db/query,multi-query,transaction,maintenance,export(route and worker),profile, the bot's/api/v1/executions, an alert's run and the agent's statement runner is scanned for the relations it names; the first one outside the scope is refused with 403 and auditedobject_forbidden, with the fix: qualify the name, spell out the wildcard, or write a statement whose reach can be read (CALL,COPY,SYSare refused as unreadable rather than guessed at). MongoDB and Redis get visibility only. - Admin form. A rule list on the datasource sheet, pattern and principals per row. A virtual datasource can neither carry rules nor include a member that has any.
- Agent. The persisted actor records the session's groups and named roles, so a drive judges the rules as the person who opened the run.
Limits, stated: the rules are the portal's; the datasource's credential still sees the whole engine, and monitoring's table statistics are not filtered yet. See docs/CONTEXT.md §4.56 and docs/SEED_CONNECTIONS.md "Object rules".
Image: ghcr.io/klinux/dbportal:0.8.0 · chart 0.1.32.
v0.7.2
Elasticsearch 6.x: no more "Invalid index name [_data_stream]"
On 6.x, GET /_data_stream and GET /_index_template are read as index names ("Invalid index name [data_stream], must not start with ''"), and that one refusal sank the whole object inventory the sidebar loads on connect, as a toast on every open.
- Composable templates arrived in 7.8 and data streams in 7.9. The transport keeps the release it already reads for the SQL path (major and minor, once per connection) and answers those two listings with an empty list, without a request, on a cluster that predates the kind. Zero is the truth there: the objects are not in its model.
- A cluster the client cannot date, or one naming another distribution, is asked and answers for itself.
See docs/providers/elasticsearch.md §3.12 and docs/CONTEXT.md §4.55.
Image: ghcr.io/klinux/dbportal:0.7.2 · chart 0.1.31.
v0.7.1
Elasticsearch 6.x
A 6.x cluster with SQL enabled refused the connect probe with HTTP 405 on /_sql: the cluster read _sql as an index name, because 6.x serves SQL at /_xpack/sql and the prefix only went away in 7.0.
- The transport reads
GET /once per connection, before the first statement, and takes/_xpack/sqlwhen the payload names no other distribution and a major below 7. OpenSearch is at 3.x, so the number alone is not enough; an Elasticsearch connection pointed at OpenSearch still gets the measured refusal of/_sql. - The cursor close follows the same path; a refusal of
/is reported as that refusal; an unreadable version takes the modern path. - The mapping read goes through the type level 6.x still has (
mappings.<type>.properties), for the single and the bulk read alike. - The routing message no longer claims the SQL endpoint, since the same envelope answers a listing 6.x has no endpoint for (
_index_template,_data_streamreport the refusal as their unavailable reason).
Measured live: the 405 on /_sql and the 401 on /_xpack/sql. The rest is replay; see docs/providers/elasticsearch.md §3.12 and docs/CONTEXT.md §4.55.
Image: ghcr.io/klinux/dbportal:0.7.1 · chart 0.1.30.
0.7.0
Seed a MySQL datasource from its schema
Seeding from the schema (Operations → Seed from schemas) was PostgreSQL only. The same plan, panel and job now run on MySQL:
- The catalog comes from
information_schema, MySQL's types mapped onto the generators (tinyint(1)/bit(1)as booleans, DATETIME as a Date, ENUM and SET labels,AUTO_INCREMENTas the identity; a generated column is never written nor copied). - What MySQL has no spelling for is done another way: no
RETURNING, so a batch's numbered keys are read back; noTRUNCATE CASCADE, so the tables are emptied withDELETEchildren first and the counters reset;?binds and a boundedINlist for a copied sample. - The schema is the datasource's own database unless named; a sample is copied from a datasource of the same engine only.
- The Seed tab now says which reason keeps a datasource out (the engine, or production).
Not verified against a live MySQL yet.
Image: ghcr.io/klinux/dbportal:0.7.0 · chart 0.1.29.
0.6.6
The environment beside every datasource's name
The same application lives in several environments under one name, and pickers that showed name and engine alone showed identical rows (two smb (mysql)). An environment tag now sits beside the name in the operations, monitoring, schema-diff, command-palette and mobile-header pickers; the seed-data and alerts option lists carry it as a suffix (smb · STAGING). A declared environment shows its label; one the list lacks shows its id.
Image: ghcr.io/klinux/dbportal:0.6.6 · chart 0.1.28.
0.6.5
MySQL account plan: what the bootstrap cannot pass on is said, not attempted
Measured on Cloud SQL for MySQL: the whole plan ran and the default user could not pass CONNECTION_ADMIN on, so the report ended on a refused optional grant. The plan now reads the global privileges the bootstrap holds with the grant option (dynamic ones included), leaves such a grant out, and shows a note before anything runs: what the account lacks (kill from the sessions panel, or the sessions of others) and the statement an administrator may run later.
Image: ghcr.io/klinux/dbportal:0.6.5 · chart 0.1.27.
0.6.4
The same datasource name in another environment
A new datasource's id was the slug of its name alone, so the same application in two environments (smb in production, smb in staging) could not be saved twice. The id is a key everywhere (seed:<id>, the Vault path, the audit target) and stays unique; the name is what people read and may repeat. When the slug is taken, the environment is appended (smb-staging), then a counter, and the toast says the id the datasource was created under.
Image: ghcr.io/klinux/dbportal:0.6.4 · chart 0.1.26.