Commit Graph
1983 Commits
Author SHA1 Message Date
Juan DenisandClaude Fable 5 c803ee5012 fix(census): measure only tracked files — the instruments count what ships
The raw-subprocess census and the thread-ownership guard walked the
filesystem under backend/app, which on a dev box also holds *installed*
extension copies under app/plugins/ that git does not track. The census
therefore measured 28 on dev machines but 24 on CI's clean checkout — four
sites of invisible ceiling slack, a stale thread classification for the
locally-installed analytics extension, and a red migration-inventory
freshness test on every CI run since the range landed.

Both instruments now intersect their walk with git ls-files (falling back
to walking everything when git cannot answer), the analytics thread entry
is removed (extension repos own their own threads), the ceiling ratchets
down 28 -> 24, and the inventory is regenerated. Proving test:
test_untracked_files_are_not_counted plants an untracked file with a raw
call inside app/ and asserts the census does not see it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 10:06:38 -04:00
Juan DenisandClaude Fable 5 8753ae6768 docs(plans): fix two pre-rebase hashes and the drifted Server query-site count
The plan-76 completion record cited a75f435f (clipboard) and 3ac566cb (first
poller wave) — pre-rebase hashes unreachable from HEAD; the landed commits
are e1a5a5e1 and 1503c493. The plan-77 record's '98 query sites' for the
Server SoftDelete tail measures 99 today, so it is recorded as approximate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 09:52:08 -04:00
Juan DenisandClaude Fable 5 0034b25a55 docs(architecture): describe the auth policy layer, typed errors, and realtime registry
Four passages still described the pre-convergence world: bare @jwt_required()
as the route guard (now the frozen legacy population behind auth_required()
and the rbac identity door), plain JSON errors (now typed envelopes with
request ids), sockets.py as a pile of handlers (now the channel registry,
room grammar, and run envelope), and deploy_log/deploy_status as the deploy
channel (now a compatibility dual-emit under run_log/run_status). Also names
the model mixin suite and canonical status vocabulary in the Models row.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 09:51:32 -04:00
Juan DenisandClaude Fable 5 06dacf8310 docs(security): current release line, drop dead audit link, describe API auth
The supported-versions table still said 1.6.x while 1.7.112 ships; the
SECURITY_AUDIT.md link pointed at a file that has never existed in git
history. Adds a short API Authentication section describing the JWT-only
default with per-route API-key opt-in through the rbac policy decorator.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 09:46:50 -04:00
Juan DenisandClaude Fable 5 ed77f169c5 docs(roadmap): bring Recently Completed current through the convergence run
Adds the user-facing work the last 107 commits shipped: managed-database
Recycle Bin, the typed error envelope with request ids and no-swallowed-
crashes guarantee, visibility-aware polling plus the unified run stream,
tunnel reconciliation on the long-poll transport, and surfaced credential
encryption failures.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 09:45:30 -04:00
github-actions[bot] 62d02682c2 chore: bump version to 1.7.112 [skip ci] 2026-08-19 12:44:53 +00:00
Juan DenisandClaude Fable 5 c5ade5a2bc docs: regenerate MIGRATION_INVENTORY (runs endpoint now on auth_required)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 06:06:08 -04:00
Juan DenisandClaude Fable 5 3dbd234eb6 fix(ratchets): reconcile plan-77 additions with the standing gates
The full-suite run tripped four ratchets on plan-77's own additions —
exactly their job: /api/v1/runs moves onto auth_required (the JWT-only
ceiling holds at 605) and behind run_log_service.list_run_logs (no
model access in the controller); the core-blueprint snapshot records
runs_bp deliberately (108 -> 109 + sha); sso_service joins the crypto
allowlist (C2 dual-read lives there by design); the controller-boundary
baseline re-records the two pre-existing ManagedDatabase crossings under
their B5 query_active spelling (count unchanged).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 06:04:59 -04:00
Juan DenisandClaude Fable 5 a3e907e97f docs(plan-77): completion record — decisions, scope calls, retained tail
All workstreams A-G executed 2026-08-19; open questions resolved
(D1 read-edge normalize, C2 permanent dual-read on the one key, B2
full-dict + _query shaping, E1 dual-emit); deliberate tail named with
owners (Server soft-delete pass, template/security_feed remote-index
non-adopters, the two raw log channels). BASELINE_COUNT 4460 -> 4591.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 05:50:35 -04:00
Juan DenisandClaude Fable 5 b07b988261 test(one-doors): route_rules fixture, create_app ratchet, authz one-liners (plan 77 G3/G4)
G3: session-scoped route_rules serves url_map checks without booting a
second app (each boot re-exposes the plan-64 state-leak classes);
test_fixture_scope_guard freezes the 13 remaining direct create_app
callers as a shrinking baseline (config_checkpoint_alias migrated as
proof). G4: assert_requires_auth/assert_admin_only promoted into
tests/factories.py; test_raw_infra_authz delegates to the shared shape.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 05:46:25 -04:00
Juan DenisandClaude Fable 5 f237d4f3b4 feat(databases): ManagedDatabase adopts SoftDelete into the Recycle Bin (plan 77 B5)
First of the regret-ranked adoptions: a plain delete now tombstones the
row (migration 088; no unique constraints, so no partial-index rewrite).
The database content and its managed BackupPolicy survive the bin window
— restore brings everything back intact; PURGE removes the policy but
NEVER drops database content; the explicit ?drop=true path stays
immediately destructive. Active read paths (list/get/adopt/manifest/env
references/app detail) switch to query_active(); the backup executor
deliberately stays tombstone-blind so bin-parked data keeps backing up.
Server (98 query sites, fleet enrollment + HMAC secrets) is the next
ranked adoption — deliberately its own pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 05:43:53 -04:00
Juan DenisandClaude Fable 5 9860c6f344 refactor(crypto): fold the parallel Fernet stacks into utils/crypto (plan 77 C2)
env_variable's class-level SECRET_KEY-derived Fernet (reached
cross-model by shared_resource) and sso_service's private stack now
WRITE through the one key path (SERVERKIT_ENCRYPTION_KEY) and dual-READ:
one path first, legacy key second — permanent window, so rows never
rewritten keep decrypting and no bulk decrypt-all/re-encrypt-all pass
ever runs (PR #94 postmortem). The '[DECRYPTION_ERROR]' sentinel and
None-on-failure token semantics are preserved.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 05:37:32 -04:00
Juan DenisandClaude Fable 5 5d875d8a09 refactor(scss): status-variant mixin — 174 re-rolled color pairs converge (plan 77 D4)
@mixin status-variant($color, $bg, $border) in _mixins.scss is the one
shape for coloring status-bearing elements; every mechanical
background/color token pair across 40+ page/component partials now
includes it (pass-through args -> identical compiled CSS values, build
green). check-status-scss.mjs holds the raw-pair count at zero and runs
inside npm run lint.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 05:35:28 -04:00
Juan DenisandClaude Fable 5 2e3a735d5a refactor(models): SerializableMixin — mechanical to_dicts become introspection (plan 77 B2)
Opt-in to_dict over __table__.columns: auto-isoformat datetimes,
*_encrypted always dropped, __serialize_exclude__ for redacted columns,
serialize_extra() for computed fields (named to dodge ServerMetrics'
literal 'extra' column). 20 purely-mechanical to_dicts across 15 files
convert; genuinely custom shapes keep their hand-written methods (plan
75 §F's ruling stands). Field selection stays api/_query.py's job.
Gate: key-for-key parity against the pre-conversion key sets frozen
from git HEAD (only the Timestamp pair may be additive).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 05:33:17 -04:00
Juan DenisandClaude Fable 5 57c0dc3956 feat(utils): CachedRemoteIndex — one remote-catalog engine (plan 77 F1)
The fetch -> TTL -> last-good -> bundled pipeline that four services
re-implemented (and that produced the bundled-index and proxy-rewrite
incidents) now lives once in utils/remote_index. The extension registry
and theme registry become thin normalizers over it; the registry gains
the failure-retry throttling the theme copy had already hardened to
(last-good under full TTL, bundled held only error_ttl). Deliberate
non-adopters, documented: template_service (keyed per-repo cache with
targeted invalidation) and security_feed (DB-durable last-good for a
daily job). Engine unit tests pin the semantics; registry/theme/signing
suites stay green (test seeds updated to the shared cache seam).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 05:27:10 -04:00
Juan DenisandClaude Fable 5 01b82955f9 feat(cache): ttl_cached decorator on CacheService; two TTL dicts migrate (plan 77 F2)
One TTL-memoization shape (Redis-backed where configured, in-memory
otherwise, None results never cached, .invalidate() per key).
install_profile_service capability probe and resource_tier_service tier
probe drop their hand-rolled module-level timestamp dicts. Ratchet with
a documented allowlist for caches whose semantics genuinely differ
(lock-guarded non-JSON snapshot, cache-forever catalog, the F1
remote-index family).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 05:20:59 -04:00
Juan DenisandClaude Fable 5 a9e76cf317 feat(frontend): useServerStream hook — one join/listen/cleanup dance (plan 77 E4)
joinServerStream()/useServerStream() own connect + join_room + listener
+ teardown, including the re-join-on-reconnect step the hand-rolled
copies missed (a reconnect silently loses server-side rooms).
JobProgressModal adopts the hook form; CloudflaredTab's imperative login
flow adopts joinServerStream. RemoteTerminal keeps its role-gated
subscribe_terminal dance (a different channel contract).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 05:14:30 -04:00
Juan DenisandClaude Fable 5 b0589e95db feat(realtime): generalized run envelope — one stream door for every run kind (plan 77 E1)
RunLogStream accepts any RunLike (__run_kind__ + id + to_dict, optional
result/step members): one room grammar run_<kind>_<id>, one batched
persisted run_log/run_status event pair, and a GET
/api/v1/runs/<kind>/<id>/logs?after_id polling twin over the new
run_log_entries table (migration 087). Deploys keep DeploymentJobLog and
dual-emit the legacy deploy_log/deploy_status during the migration
window. The unified Job runtime — which had NO live channel — adopts it
in the consumer (start/succeeded/failed frames; handlers can log via
stream_for(job)). The dead pipeline_* scaffolding was retired in E2.
Gate: emit -> persisted row -> after_id resync contract test; consumer
adoption test. MIGRATION_INVENTORY.md regenerated (census moved).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 05:13:02 -04:00
Juan DenisandClaude Fable 5 bae6e18578 test(deploy-console): drive the registry-generated subscribe handler (plan 77 E2)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 05:05:11 -04:00
Juan DenisandClaude Fable 5 c8a5d3a39c refactor(realtime): declarative channel registry; dead pipeline_* retired (plan 77 E2)
register_channel() generates each subscribe/unsubscribe pair with ONE
auth model (connected-identity base check + optional per-channel gate)
and rooms built on sockets_rooms; metrics, container_status, terminal
and deploy convert. The pipeline_* channel (zero callers anywhere) is
deleted. Gate: per-channel auth tests driving the generated handlers;
ratchet freezes the two remaining raw handlers (file/container logs,
which own per-sid OS resources).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 05:04:28 -04:00
Juan DenisandClaude Fable 5 ff501b51ad feat(realtime): BackgroundLoop helper; sockets broadcasters adopt it (plan 77 E5)
One shape for process polling loops: Event-based stop (immediate, not
sleep-bounded), lock-guarded idempotent start/stop, optional app-context
per tick, real logger error handling (the sockets loops reported errors
via print() — unreadable in prod), and a run_while predicate for
subscriber-scoped loops. The metrics and container-status broadcasters
convert; thread_ownership now carries ONE entry for the helper's spawn
site instead of a row per hand-rolled thread.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 05:01:10 -04:00
Juan DenisandClaude Fable 5 00eac38d5d feat(realtime): one room grammar + mirrored event constants (plan 77 E3)
app/sockets_rooms.py owns every Socket.IO room-name builder (the grammar
lived in ~25 scattered f-strings); app/constants.py SOCKET_EVENTS is
mirrored verbatim by frontend/src/constants/events.js and a contract
test parses the JS so a renamed event fails in CI instead of silently
killing a stream. sockets.py / agent_gateway / notification bus and the
two frontend components that hand-derived rooms now use the builders;
ratchet forbids new room f-strings in the realtime layer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 04:58:49 -04:00
Juan DenisandClaude Fable 5 f550ddc04f refactor(deploy): deployment_runner rides RunLifecycleMixin transitions (plan 77 D2)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 04:55:13 -04:00
Juan DenisandClaude Fable 5 0e1ebee601 refactor(deploy): DeploymentJob adopts RunLifecycleMixin (plan 77 D2)
The 11 hand-written status+completed_at(+error_message) transition
blocks in deployment_job_service collapse into mark_running/
mark_succeeded/mark_failed. The model keeps its stored 'succeeded'
spelling via the mixin override until this domain's data migration;
error text routes through __error_attr__ = 'error_message'.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 04:54:38 -04:00
Juan DenisandClaude Fable 5 ba09ec75b6 chore(frontend): wire status one-door ratchet into npm run lint (plan 77 D3)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 04:50:15 -04:00
Juan DenisandClaude Fable 5 dc32ac6e86 refactor(frontend): one status->tone vocabulary in ds/status.js (plan 77 D3)
statusKind()/statusVariant()/statusDotClass()/statusLabel() drive both
Pill kinds and Badge variants from ONE table; ~40 per-page maps across
35+ files migrate to it. Scoped overrides stay in the same file:
serviceStatusKind (running IS the healthy steady state for services/
units/previews -> green), processStateVariant, alertStatusKind,
envActionKind, impactTone. Unifications: run-shaped 'running' is cyan
everywhere (Git.jsx's amber gone), QueueDetail/QueueOperations' invalid
blue/yellow kinds fixed, severity tones (critical/high/medium/low)
agree across security surfaces. Ratchet: npm run check:status fails on
new local maps outside ds/status.js (frozen allowlist for domain maps
and D4-territory hex/css-class renderers). Lint + build green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 04:49:35 -04:00
Juan DenisandClaude Fable 5 e810016005 refactor(models): finish JsonColumnMixin adoption — no hand-rolled json.loads (plan 77 B4)
~60 call sites across 24 model files now ride _json_read with each
site's exact default preserved; 34 classes newly adopt the mixin
(registry contract test expanded to 36). Reverses the old 'bare sites
stay raising' ruling: a corrupt row degrades to its default instead of
500ing every serializing endpoint (docstring updated). Ratchet:
json.loads( appears only in json_column_mixin.py.

Known pre-existing: test_host_inventory enumerate_filesystems failures
on this machine (verified via clean-tree stash rerun).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 02:15:09 -04:00
Juan DenisandClaude Fable 5 5d69154002 feat(utils): env one-door — env_str/env_bool/env_int (plan 77 F4)
config.py's parsers move to app/utils/env.py and are imported back; the
three incompatible truthiness variants collapse to one TRUTHY tuple.
Fixes the measured bug: SERVERKIT_ALLOW_PRIVATE_DOWNLOADS=on silently
read false in plugin_service (its tuple lacked 'on'); same for the pip
gate, SERVERKIT_STAGING, and SERVERKIT_DEMO_MODE. Ratchet: no new
os.environ truthiness tuples outside utils/env.py.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 02:12:45 -04:00
Juan DenisandClaude Fable 5 a66c4f8164 refactor(security): one sensitive-keyword union for all four redaction sinks (plan 77 F3)
utils/sensitive_data_filter.SENSITIVE_KEY_PARTS is now the union of the
four diverging lists (response filter, audit_service, middleware/audit —
previously the only sink covering totp/otp/csrf/session — telemetry);
all sinks delegate to the shared is_sensitive_key(), and mask_payload()
gives log sinks a scope-free recursive masker. Safe-key exceptions keep
identifier-shaped fields (private_url, certificates, totp_enabled, …)
readable. Ratchet: no SENSITIVE list literals outside the one door.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 02:10:22 -04:00
Juan DenisandClaude Fable 5 1b49352f27 feat(models): canonical status vocabulary + RunLifecycleMixin (plan 77 D1/D2)
models/status.py defines the six lifecycle constants, TERMINAL/ACTIVE
sets, and normalize() mapping every measured legacy spelling (succeeded/
completed/done/failure/error/canceled/scheduled) home at the read edge —
stored strings migrate per-domain later, never wholesale.
RunLifecycleMixin centralizes mark_running/succeeded/failed/cancelled
transitions (status + started_at/completed_at/duration), with per-model
spelling overrides so adoption never silently rewrites a domain's stored
vocabulary. Parity gate: every measured spelling normalizes canonical.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 02:06:05 -04:00
Juan DenisandClaude Fable 5 745bc2b5d9 refactor(agent): transport-neutral handshake + state ingest doors (plan 77 A2)
agent_registry.authenticate_and_register owns the full handshake
(rate-limit -> HMAC -> allowed-IPs/anomaly -> new-IP -> version ->
register) and ingest_agent_state owns heartbeat/sysinfo/capability
side-effects; the WS namespace and long-poll endpoints become thin
adapters translating error codes to emit/disconnect vs jsonify/status.
The per-IP auth limiter moves to the registry (re-exported from the
gateway for existing callers/tests). Gate: parametrized ws/poll matrix
(bad HMAC, blocked IP, rate limit, new-IP anomaly) + a word-anchored
ratchet that the adapters never re-grow the sequence.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 02:04:29 -04:00
Juan DenisandClaude Fable 5 4919ff1cfe refactor(models): TimestampMixin + uuid_pk() one-door shapes (plan 77 B1/B3)
TimestampMixin (byte-identical to the hand-written pair — zero schema
migrations) adopted by 52 classes across 44 model files; the utcnow
tz-aware migration is now a one-file edit. uuid_pk() replaces the 13
pasted String(36) uuid4 lambdas across 7 files (ai.py's hex variant
stays grandfathered, documented). Deviant shapes (indexed created_at,
sanitization_profile's default-less updated_at, created-only models)
deliberately keep their own columns.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 01:59:10 -04:00
Juan DenisandClaude Fable 5 9b9aa34f01 test(seeds): one-door factories + persona headers + adoption ratchet (plan 77 G1/G2)
tests/factories.py owns make_user/headers_for/make_application/
make_server/make_workspace; conftest personas (scoping_rbac,
new viewer_headers/developer_headers fixtures) ride them. The
byte-identical _seed_app pair (container scale/sleep), two variants
(image_update, waf) and three server fixtures (fleet doctor/repair)
migrate as proof. Ratchet freezes the 44-file hand-minted-JWT and
65-file direct-Application() populations.

Note: test_fleet_doctor.py::test_dns_fail_when_unresolved fails
pre-existing on this machine (warn vs fail), unrelated to this change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 01:55:18 -04:00
Juan DenisandClaude Fable 5 d2525717a8 refactor(crypto): EncryptedSecret adoption sweep + service-level crypto ratchet (plan 77 C1)
Six more models own their *_encrypted columns through the descriptor
(linked_panel, source_connection, container_registry, managed_database,
registrar_connection, pending_agent); eight service/api files stop
importing utils.crypto for them. legacy_plaintext mode mirrors
decrypt_secret_safe so pre-encryption rows keep reading until rewritten
— this also fixes registrar/source-connection reads that used the
RAISING decrypt variant on columns that may hold legacy plaintext.
Ratchet test freezes the remaining service-level crypto importers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 01:49:42 -04:00
Juan DenisandClaude Fable 5 a3f1cc9766 feat(models): EncryptedSecret descriptor; server.py stops swallowing encrypt failures (plan 77 C1)
One crypto path for model-owned *_encrypted columns: assignment encrypts
and RAISES on failure (server.py's accessors print()-ed the error and
silently stored nothing — an agent could 'pair' yet never verify a
signature); read decrypts tolerantly (None + logged warning). is_set()
standardizes the has_secret masking flag. Server adopts it for
api_secret / api_secret_pending; legacy method names kept as delegates.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 01:45:12 -04:00
Juan DenisandClaude Fable 5 99d1c86ebb fix(agent): tunnel reconcile fires on both transports (plan 77 A1)
The WireGuard tunnel-reconcile hook lived only in the WS on_capabilities
handler, so an agent connected via the long-poll fallback never
self-healed its tunnels after a restart. Move the hook into
agent_registry.update_capabilities — the ingest path both transports
already share — and prove it with poll-path + registry-path tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 01:42:13 -04:00
github-actions[bot] 766cd47fcc chore: bump version to 1.7.111 [skip ci] 2026-08-19 05:37:24 +00:00
Juan Denis aa522186c9 Merge branch 'dev' of https://github.com/jhd3197/ServerKit into dev 2026-08-19 01:37:09 -04:00
Juan DenisandClaude Fable 5 5aab23c19c docs(plan-76): completion record — decisions, commits, retained duplication
Names the resolved A and C decisions, the 2026-08-19 commit set, the
intentionally retained semantic duplication with owners and removal
conditions, and the deferred H items. Collection floor 4430 -> 4460.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 00:21:20 -04:00
Juan DenisandClaude Fable 5 3953090526 feat(conformance): generated remaining-migration inventory (H closure)
docs/MIGRATION_INVENTORY.md is generated from the live censuses and the
frontend boundary checker's new --inventory JSON mode — no hand-typed
numbers, so the plan cannot claim completion while a door is bypassed. A
freshness test fails whenever a ratchet number moves without regenerating
the document, making the regeneration part of every migration commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 00:16:39 -04:00
Juan DenisandClaude Fable 5 39a6e35d42 test(contracts): ratchet the four remaining adoption populations
Backend: hand-shaped {'error': ...} bodies in app/api get an AST census
and ceiling (1159) — milestone B's migrate-when-touched policy made
enforceable. Frontend: check-frontend-boundaries.mjs gains adoption
ceilings for raw api.* calls in pages (460, E1), per-page toast.error
extraction (264, E1), form-group blocks (343, F2), and unencoded query
interpolations in services/api (97, C4-interim) — totals that may only go
down, with a slack guard so a ceiling cannot drift above reality.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 00:13:59 -04:00
Juan DenisandClaude Fable 5 eb83d332e7 test(auth): ratchet the JWT-only population and record the A decision
Milestone A's open question is decided: JWT-only stays the deliberate
default. Converting a route to auth_required() grants API-key access, so
it happens one route at a time when a use case exists, in a commit that
says so — never as a sweep. The new census/ceiling (605) enforces both
directions: no new bare @jwt_required() routes, and no silent
conversions. The identity ratchet's third assertion keeps each deliberate
conversion safe.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 00:11:16 -04:00
Juan DenisandClaude Fable 5 5d7fa225a6 refactor(list-queries): one PageQuery door for the page/per_page dialect (C3)
QueryParseError is now a ValidationError subclass — the global handler
answers it as a 400 and per-route translations become unnecessary (the
existing ones keep working). PageQuery parses, defaults, and bounds
page/per_page in one place; admin audit-logs, activity feed, error logs,
and telemetry events adopt it (telemetry keeps its 200 cap). git.py and
source_connections.py keep hand-parsed params deliberately — they proxy
pagination to Gitea/GitHub/GitLab/Bitbucket, not to DB queries.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 00:10:09 -04:00
github-actions[bot] 237b3f6f00 chore: bump version to 1.7.110 [skip ci] 2026-08-19 04:07:56 +00:00
Juan Denis bd35ce7774 Merge branch 'dev' of https://github.com/jhd3197/ServerKit into dev 2026-08-19 00:07:37 -04:00
Juan DenisandClaude Fable 5 118f90790e fix(webhooks): record the crash the PR-webhook 200 was hiding
The inbound PR webhook must keep answering 200 (providers disable failing
hooks), but its preview-settings crash only reached logger.debug — the
last route swallowing an exception without putting it on the record. It
now goes through record_unexpected before the in-band answer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 00:05:33 -04:00
Juan DenisandClaude Fable 5 b7b8b38773 refactor(db-tuner): typed errors close the status-sniffing census at 0
DbConfigTunerService raises ValidationError (preconditions/settings),
DependencyUnavailableError (engine/container operations, with the
restore-then-report semantics preserved in the message), and NotFoundError
(nothing to roll back to); db_tuner.py drops its three error-key
ternaries. The census docstring now records the completed migration and
corrects its own stale 'eight callers' blocker. Census 3 -> 0 — held as an
invariant from here.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 00:03:50 -04:00
Juan DenisandClaude Fable 5 6337066fad refactor(secrets): vaults + webhook gateway onto the typed-error contract
SecretVaultService, SecretService, and WebhookGatewayService raise typed
errors and return bare data; secrets_webhooks.py drops all twelve
{'success': bool} translations including its two prose sniffs. Bulk
create keeps per-item error collection (catching ApplicationError).
Replay now answers 200 with success:false when the forward target refuses
— previously a failed forward was a 404 with error:null — and the
WebhooksTab toast distinguishes the two. The public /webhooks/receive
wire protocol is intentionally unchanged (external senders depend on it).
Sniffing census 6 -> 4.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 00:00:36 -04:00
Juan DenisandClaude Fable 5 28dcd63f4d refactor(themes): registry install raises typed errors
theme_registry_service.install returns the theme dict and raises
NotFound/Validation/DependencyUnavailable; the route's not-found prose
sniff is gone. import_theme keeps its tuple contract for the import
route. Sniffing census 7 -> 6.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 23:56:15 -04:00
Juan DenisandClaude Fable 5 fbc36831b1 refactor(ddns): typed errors replace the token-prose sniff
update_ip raises AuthenticationError for a bad/disabled token (401 via the
global handler, as before) and ValidationError for a bad IP; the route's
except-ValueError message sniff is gone. Sniffing census 8 -> 7.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 23:55:17 -04:00