26 Commits
Author SHA1 Message Date
sindri 96c4576a8e deploy: thoth b07fc8b361 2026-10-31 10:57:14 +00:00
MarioGLealP b07fc8b361 Merge pull request 'fix(catalog): whole-system sources only report their own item types' (#5) from marioglealp/shikigami-types into main
ci / checks (push) Successful in 48s
ci / build (push) Successful in 49s
Reviewed-on: #5
2026-10-31 10:55:33 +00:00
MarioGLealPandClaude Opus 5.5 b63d524463 fix(catalog): whole-system sources only report their own item types
ci / checks (pull_request) Successful in 33s
ci / build (pull_request) Successful in 8s
A source without a scope rule could create any item type, empty, under
its name (Shikigami could declare a host). Fields don't settle it: some
types have none. So whole-system sources get a type list, written as
each one is built (D51); Shikigami's is app, route and bot. Sources
without a list aren't limited yet - none reports anything so far.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MdS1HcT3JpVno881Z46fei
2026-10-31 10:46:47 +00:00
MarioGLealPandClaude Opus 5.5 db976e2eb8 deploy: nuwa-app 0.4.0 (shared meru-eso role)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MdS1HcMerN6toVuLY5K4MR
2026-10-30 15:11:48 +00:00
MarioGLealPandClaude Opus 5.5 bf99fb2883 deploy: nuwa-app 0.3.0, thoth-db-runtime filled by ESO from OpenBao
ESO takes the hand-made Secret over with the same values (OpenBao holds
a copy since meru-eso.sh); CNPG's managed role keeps reading it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MdS1HcMerN6toVuLY5K4MR
2026-10-30 09:08:11 +00:00
sindri c95c137ab7 deploy: thoth 3455a01194 2026-10-29 23:09:20 +00:00
MarioGLealPandClaude Opus 5.5 3455a01194 docs: how Meru deploys Thoth (deploy/meru.yaml, deploy/database/)
ci / checks (push) Successful in 34s
ci / build (push) Successful in 51s
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MdS1Hfahkh27nfbtRVZ4CR
2026-10-29 23:07:50 +00:00
MarioGLealPandClaude Opus 5.5 1e24a5d34a deploy: mark Thoth as a Meru app; its database moves here from clockmost-infra
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MdS1Hfahkh27nfbtRVZ4CR
2026-10-29 22:59:36 +00:00
sindri 73ab8a34fd deploy: thoth 82c01d43c0 2026-10-29 20:00:03 +00:00
MarioGLealP 82c01d43c0 Merge pull request 'ci: fetch the Zot login and signing key from OpenBao' (#4) from marioglealp/openbao-keys into main
ci / checks (push) Successful in 31s
ci / build (push) Successful in 50s
Reviewed-on: #4
Rd
2026-10-29 19:58:40 +00:00
MarioGLealPandClaude Opus 5.5 dde99cff78 ci: fetch the Zot login and signing key from OpenBao
ci / checks (pull_request) Successful in 31s
ci / build (pull_request) Successful in 9s
The build job (Meru/sindri 0.2.0: signer account, OpenBao-only token,
in-memory /keys) logs in as meru-thoth-build, writes the two keys to
/keys, revokes its token; sign reads /keys. Same as Meru/echo's.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MdS1Hfahkh27nfbtRVZ4CR
2026-10-29 18:49:02 +00:00
sindri d0d840b502 deploy: thoth 4e3be7029e 2026-10-29 15:28:05 +00:00
MarioGLealP 4e3be7029e Merge pull request 'feat: deploy values and the CI deploy step' (#3) from marioglealp/deploy into main
ci / checks (push) Successful in 30s
ci / build (push) Successful in 51s
Reviewed-on: #3
2026-10-29 15:26:36 +00:00
MarioGLealPandClaude Opus 5.5 b4fa177627 feat: deploy values and the CI deploy step
ci / checks (pull_request) Successful in 31s
ci / build (pull_request) Successful in 10s
deploy/values.yaml for nuwa-app 0.2.0: readiness on /ready, the app as
thoth_app and migrations as the owner (both from CNPG's Secrets via
PGUSER/PGPASSWORD - no password in any URL), Authentik settings from
D57. Starts on the signed image of 78839e5. Main builds now write their
digest here, as Echo's.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MdS1Hfahkh27nfbtRVZ4CR
2026-10-29 15:23:10 +00:00
MarioGLealP 78839e5ce6 Merge pull request 'feat: the app runs as thoth_app - rows only, never history or schema' (#2) from marioglealp/app-role into main
ci / checks (push) Successful in 32s
ci / build (push) Successful in 49s
Reviewed-on: #2
2026-10-29 15:00:39 +00:00
MarioGLealPandClaude Opus 5.5 ecaf921cef feat: the app runs as thoth_app - rows only, never history or schema
ci / checks (pull_request) Successful in 34s
ci / build (pull_request) Successful in 8s
The running app logs in as its own role (D58): SELECT/INSERT/UPDATE on
the live tables, SELECT on history and the migration record. It owns
nothing, so it can't change the schema or switch off a trigger; it has no
DELETE (only Charon's purge deletes). The change-tracking function now
runs as its owner (SECURITY DEFINER, fixed search_path), so it alone
writes history - the app can't forge a version.

Grants are alembic_utils entities (src/thoth/sql/grants.py), columns
from the models, so autogenerate keeps them in step. tests/test_roles.py
holds its own copy of the matrix (a new table fails until it's decided)
and proves the refusals: history insert, delete, trigger switch-off,
drop, function replace, create table.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MdS1Hfahkh27nfbtRVZ4CR
2026-10-29 14:59:38 +00:00
MarioGLealPandClaude Opus 5.5 29d08d2ba1 ci: checks and image build on Sindri
ci / checks (pull_request) Successful in 29s
ci / build (pull_request) Successful in 8s
ci / checks (push) Successful in 28s
ci / build (push) Successful in 49s
- .gitea/workflows/ci.yaml: `checks` (the whole of scripts/check.sh in a
  credential-free pod, Postgres 16.15 as a service container) then
  `build` (rootless BuildKit; main also pushes to Zot and signs).
- scripts/tools.py: vacuum and oasdiff as sha256-pinned release binaries,
  the same locally and in CI (Sindri has no podman); check.sh uses them.
- tests fail instead of skipping in CI when there's no database.
- Dockerfile: dependencies layer first, the app non-editable, migrations
  + alembic config in the image, uid 10001, no .pyc (read-only root).
- test-db.sh pins Postgres 16.15 by digest (what CNPG runs).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MdS1Hfahkh27nfbtRVZ4CR
2026-10-29 14:46:01 +00:00
MarioGLealPandClaude Opus 5.5 c658f640fa feat: callers need an Authentik token and a permission
Tokens checked per the security standard (issuer, audience, algorithm,
JWKS key, expiry/not-before, required claims); permissions thoth.read,
thoth.report:<source>@<scope> (must match the scan's path), thoth.manual,
thoth.verdict. Changes are recorded under the bot's name. Retiring now needs
the verdict flag the API sets only for thoth.verdict, checked by the
trigger. House rule: every operation but the probes declares security.

API-Break: every route but the probes now needs a token - nothing has called Thoth yet
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MdS1Hfahkh27nfbtRVZ4CR
2026-10-29 13:10:07 +00:00
MarioGLealPandClaude Opus 5.5 614d134b29 feat: history reads
GET /v1/items/{id}/history lists every version newest first with its
validity window, who and why; GET /v1/items?as_of= returns the inventory as
it was at that moment from the current rows and the history tables.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MdS1Hfahkh27nfbtRVZ4CR
2026-10-29 12:59:35 +00:00
MarioGLealPandClaude Opus 5.5 9362cf90f8 feat: one web endpoint with both sides
GET /v1/items/{id}/web returns impact and dependencies together (or one
side with ?direction=), cycles from both sides listed once each.

API-Break: /impact and /dependencies merge into /web - nothing has called Thoth yet
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MdS1Hfahkh27nfbtRVZ4CR
2026-10-29 12:57:21 +00:00
MarioGLealPandClaude Opus 5.5 0e9413bae8 feat: item reads and the dependency web
GET /v1/items (filters, paged) and /v1/items/{id}; /impact and
/dependencies walk the one-hop links in one recursive query: shortest
distance per item with the link it came by, loops reported as cycles
and not followed, retired links skipped, depth 1-50.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MdS1Hf1rZwApuTroJVE19S
2026-10-29 12:52:01 +00:00
MarioGLealPandClaude Opus 5.5 897e0e94fb feat: renames
POST /v1/renames: same item, relations and slices, new key; the trigger
keeps the old key in history. Only the first reporter or manual may rename;
refused for unknown, retired or taken keys.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MdS1Hf1rZwApuTroJVE19S
2026-10-29 12:47:36 +00:00
MarioGLealPandClaude Opus 5.5 df1c7ded37 feat: links in scans, scan ids, things that come and go
Scans split into items then relations (D54): PUT
/v1/scans/{scan_id}/{source}/{scope}/items and .../relations. Relation
observations per source and scope with the same strict slices, misses and
Anubis queue; a link is in scope if its from end is; links to items that
don't exist yet are skipped and heal on the next scan. Misses count once
per scan id, so resent snapshots change nothing. Devices and attached-to
wait 30 days unseen instead of 3 scans.

API-Break: /v1/snapshots/{source}/{scope} becomes /v1/scans/... - nothing has called Thoth yet
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MdS1Hf1rZwApuTroJVE19S
2026-10-29 12:28:32 +00:00
MarioGLealPandClaude Opus 5.5 3a52f14210 feat: snapshots, scopes, decommissioning, only Anubis retires
PUT /v1/snapshots/{source}/{scope}: strict slices (a missing owned field
refuses the whole snapshot), scope rules for host-scan and k8s-scan,
precedence merge across sources and scopes, miss counting with Anubis's
list at 3, a snapshot_run log for Zabbix. The history trigger refuses
status retired unless the actor is anubis.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MdS1Hf1rZwApuTroJVE19S
2026-10-29 12:16:54 +00:00
MarioGLealPandClaude Opus 5.5 c6cb9af386 feat: the catalog, readiness and /v1
17 item and 14 relation types as Pydantic classes with per-field owners in
precedence order; slices, merge, catalog.json with a breaking-change check
(Catalog-Break trailer); /ready checks every stored row against the catalog
before syncing it. Client routes under /v1.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MdS1Hf1rZwApuTroJVE19S
2026-10-29 11:50:07 +00:00
MarioGLealPandClaude Opus 5.5 3b31d7a57f feat: data model, change tracking and API contract checks
Items, relations, per-source observations and the type catalog as SQLModel
tables; history, actor/reason stamping, append-only history and the delete
guard as Postgres triggers managed by alembic_utils. FastAPI skeleton with
RFC 9457 problem details, vacuum house rules and oasdiff breaking checks.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MdS1Hf1rZwApuTroJVE19S
2026-10-29 11:34:12 +00:00