canned-prompts/service/alembic.ini

28 lines
387 B
INI
Raw Normal View History

CANP-WP-0006 T01-T02: service skeleton and tenant-keyed schema The foundation of the hosted registry, in canned-prompts so rapp.yaml gets ownership_repo: canned-prompts — the sbom-nexus shape, where product ownership stays out of the operations repo. Stack matches state-hub and sbom-nexus: FastAPI, SQLAlchemy, Alembic, PostgreSQL, in service/ with its own environment. reference/ is deliberately untouched: it is the format's conformance witness and stays dependency-light, and the service is a separate consumer of the same package semantics. CANNED_PROMPTS_DATABASE_URL has no default. A service that silently falls back to a local database when its real one is misconfigured is worse than one that refuses to start. Health surface per RailianceAppDeploymentGuide.md: unauthenticated /healthz and /readyz, plus /state/health for fleet consistency. /healthz deliberately checks nothing beyond the process being up, so a database blip does not restart pods; /readyz asks the database something it can fail to answer. Migration 0001 creates package_versions, package_files and index_entries, every one carrying a tenant key per business-app-service-contract section 1.3 — the service is single-tenant today, and the key is present so a later consolidation is a data copy rather than a rewrite. A test asserts every table in the metadata is tenant-keyed, so adding an unkeyed table fails the suite rather than being discovered at consolidation time. Uniqueness is (tenant, registry, package_id, version): registry-scoped because identity is, tenant-scoped so two tenants may hold the same id. The schema keeps the format's three things distinct — an immutable package version, its files as content rather than parsed rows, and an index entry recording how a version arrived here. Fixes a bug its own test caught: check_readiness first caught every failure in one except and reported "database unreachable", so an unmigrated but perfectly reachable database sent an operator to credentials and networking when the fix was alembic upgrade. Connectivity and schema are now checked separately. Service tests 11 passing; reference tests unaffected at 99. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Bjefh8NUiEiahN4JLwoSKM Assistant: claude-code Assistant-Model: opus Assistant-Process: 388925@bnt-lap001 Assistant-Session: 3507023f-e0fd-4a1e-9d90-a0d4217d1502
2026-09-06 19:57:47 +02:00
[alembic]
script_location = migrations
prepend_sys_path = src
CANP-WP-0006 T04: publish API POST /packages takes {registry, source, files}, where files is the same shape GET /archives returns — so an archive round-trips into a publish without translation and a mirror is a GET followed by a POST. A test verifies the round-trip by digest rather than asserting it. Validation is the reference implementation's, applied to the posted files materialized in a temporary directory. Only reserved paths and manifest-referenced files are stored (section 2), path traversal is refused, identical re-publishes are accepted, and different content under the same id@version is 409 (section 17). The identity mechanism, stated plainly rather than implied: a single shared bearer token proving the caller is the operator of this service. It is not per-publisher identity — every token holder is indistinguishable — and auth.py says so where someone might otherwise assume more. With no token configured the service is read-only. That is the correct default rather than an inconvenience: section 20.1 asks a registry to refuse publication into a closed namespace it does not consider the publisher to own, and an unauthenticated service considers nobody to own anything. Namespace claims live in namespace_claims (migration 0002) and are enforced here, which a filesystem registry cannot do at all — but only as precisely as the identity allows. A closed namespace is protected from anonymous callers; it cannot be attributed among several publishers. Per-publisher identity is deferred and is the main thing between this and a registry several people can publish to. Migration hygiene found while adding 0002: autogenerate proposed an ALTER COLUMN TYPE on package_files.package_version_id, because the foreign key's type was left to inference and compared as a variant against a reflected plain type. SQLite cannot alter a column type, so 0002 failed halfway — table created, revision unstamped, the partially-applied state that is worst to debug later. Fixed at the cause: the column is typed explicitly, and 0001 was corrected rather than patched over, which is legitimate only because it has never run outside this repo's tests. alembic check now reports no drift. Health tests now compute the expected migration head from the script directory instead of hardcoding it, so adding a migration cannot fail them spuriously. Service tests 22 -> 33; reference unaffected at 99. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Bjefh8NUiEiahN4JLwoSKM Assistant: claude-code Assistant-Model: opus Assistant-Process: 388925@bnt-lap001 Assistant-Session: 3507023f-e0fd-4a1e-9d90-a0d4217d1502
2026-09-06 20:30:00 +02:00
path_separator = os
CANP-WP-0006 T01-T02: service skeleton and tenant-keyed schema The foundation of the hosted registry, in canned-prompts so rapp.yaml gets ownership_repo: canned-prompts — the sbom-nexus shape, where product ownership stays out of the operations repo. Stack matches state-hub and sbom-nexus: FastAPI, SQLAlchemy, Alembic, PostgreSQL, in service/ with its own environment. reference/ is deliberately untouched: it is the format's conformance witness and stays dependency-light, and the service is a separate consumer of the same package semantics. CANNED_PROMPTS_DATABASE_URL has no default. A service that silently falls back to a local database when its real one is misconfigured is worse than one that refuses to start. Health surface per RailianceAppDeploymentGuide.md: unauthenticated /healthz and /readyz, plus /state/health for fleet consistency. /healthz deliberately checks nothing beyond the process being up, so a database blip does not restart pods; /readyz asks the database something it can fail to answer. Migration 0001 creates package_versions, package_files and index_entries, every one carrying a tenant key per business-app-service-contract section 1.3 — the service is single-tenant today, and the key is present so a later consolidation is a data copy rather than a rewrite. A test asserts every table in the metadata is tenant-keyed, so adding an unkeyed table fails the suite rather than being discovered at consolidation time. Uniqueness is (tenant, registry, package_id, version): registry-scoped because identity is, tenant-scoped so two tenants may hold the same id. The schema keeps the format's three things distinct — an immutable package version, its files as content rather than parsed rows, and an index entry recording how a version arrived here. Fixes a bug its own test caught: check_readiness first caught every failure in one except and reported "database unreachable", so an unmigrated but perfectly reachable database sent an operator to credentials and networking when the fix was alembic upgrade. Connectivity and schema are now checked separately. Service tests 11 passing; reference tests unaffected at 99. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Bjefh8NUiEiahN4JLwoSKM Assistant: claude-code Assistant-Model: opus Assistant-Process: 388925@bnt-lap001 Assistant-Session: 3507023f-e0fd-4a1e-9d90-a0d4217d1502
2026-09-06 19:57:47 +02:00
[loggers]
keys = root
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
qualname =
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s