Promote dev to main: v0.1.0 changelog, bundled-binary CVE note, README screenshots - #49
Merged
Merged
Conversation
…us build) (#18) * ci: publish scrye image to Docker Hub on tagged releases and dev pushes Add .github/workflows/publish.yml with two independent publishing paths: - semver tags (v*.*.*) whose commit is on main build the multi-arch (amd64/arm64) image and push <dockerhub-user>/scrye:<version> + :latest - pushes to dev push the moving <dockerhub-user>/scrye:dev continuous-build tag Extract the multi-arch build (QEMU + Buildx + build-push against docker/Dockerfile) into a reusable .github/actions/build-image composite action and refactor ci.yml's multi-arch build-check to consume it, so the build is defined in one place. Publishing uses the DOCKERHUB_USERNAME/ DOCKERHUB_TOKEN repo secrets; ci.yml still never publishes. Update CLAUDE.md and docs/PLAN.md (locked decision 0.6, §9.1, §13, Deviations) and add a CONTRIBUTING.md Releasing section describing both paths. * ci: gate multi-arch build-check to main pushes and PRs to main The image-multiarch job's arm64 leg builds the whole Dockerfile under QEMU emulation, which on a cold type=gha cache takes hours. Only main-scoped runs reliably restore a warm arm64 cache; dev-based PRs rebuild from scratch every time. Gate the check to main pushes and PRs whose base is main. Multi-arch buildability stays proven for dev by publish.yml (builds amd64+arm64 on every dev push and release tag), and dev PRs still run the fast amd64-only image build + dogfood self-scan, so no coverage is lost.
The :dev Docker Hub publish was triggered by on: push: branches: [dev], which fired on any commit reaching the dev ref — including conflict- resolution pushes to an open, unmerged promotion PR. Re-scope it to on: pull_request: types: [closed] with base dev, gated on pull_request.merged == true, and build the merged commit (merge_commit_sha). The :dev tag now publishes only when a PR is actually merged into dev. The tagged-release path (v*.*.* on main) is unchanged. Sync docs/PLAN.md (§0.6 + Deviations entry) and the CONTRIBUTING.md Releasing section to describe the merged-PR trigger.
Comprehensive report-only audit covering infrastructure/deployment, backend security, scanner orchestration, API/data/performance, frontend, feature completeness against docs/PLAN.md, and the previously-logged known limitations. Findings are stably ID'd with file:line references, CONFIRMED/PLAUSIBLE confidence markers, failure scenarios, and fix directions, plus a prioritized action list.
…webhook URLs (#23) Addresses the P0 tier of docs/reviews/full-audit-2026-07-05.md §10: - QUA-1: cap API-token minting against the caller's effective (token-capped) role, not the owner account's role, so a low-privilege token belonging to an admin can no longer mint an admin token. Adds a regression test. - API-2: run database restore (scrypt + full-DB rebuild) in a threadpool so /healthz stays responsive and the container can't be killed mid-restore. - API-3: chunked executemany restore inserts; yield_per streaming build; drop the redundant bundle re-parse; log + document the in-memory size ceiling. - API-10: raw-artifact files don't travel in a bundle, so exclude the artifacts table from the dump and clear it on restore (no dangling file references). - API-11: refuse restore (409) while a scan is queued or running. - SEC-1: treat a generic webhook URL as a write-only credential (encrypted, masked on read) like Discord; frontend renders it as a password field. Deviation logged in docs/PLAN.md; README backup section updated.
* perf(worker,api): P1 audit remediation — off-loop heavy work + bounded memory Addresses the P1 tier of docs/reviews/full-audit-2026-07-05.md §10 (availability and performance under real data volume): - API-5: off-load the scan worker's result persistence (10k+ findings flush + raw-JSON write) to a thread via anyio.to_thread, completing the systemic "no synchronous heavy work on the event loop" fix begun in P0 (API-2/3). - SCN-1: cap captured scanner stdout (SCRYE_SCANNER_MAX_OUTPUT_BYTES, default 512 MiB); output past the budget kills the child and fails the scan instead of buffering unbounded JSON. - API-4: read SBOM/backup uploads through read_upload_capped so an oversized body is rejected by reported size / chunked read, never fully buffered first. - API-7: dashboard/metrics load only needed columns per target (load_only) and serve from a short process-wide TTL cache, cleared on app startup and in tests. - API-1: eager-load scan tags (selectinload) in the two list endpoints. - API-15/API-6: run the maintenance tick (schedules + retention) off the event loop and batch retention deletes into one DELETE ... WHERE id IN (...). New tests cover the output cap and upload cap; the dashboard cache TTL/reset is tested. Deviation logged in docs/PLAN.md; .env.example regenerated. * ci,fix(config,compose): P2 audit remediation — supply chain + deployment hardening Addresses the P2 tier of docs/reviews/full-audit-2026-07-05.md §10: - SCN-3: parse the documented comma-separated env form for cors_origins and filesystem_scan_roots (NoDecode + a before-validator), so the filesystem-scan enable switch (SCRYE_FILESYSTEM_SCAN_ROOTS=/path) no longer fails at startup. Adds env-parsing tests. - INF-1: add .github/dependabot.yml (github-actions ecosystem, weekly, grouped). SHA-pinning each `uses:` needs current action SHAs, which this environment's egress policy blocks from resolving/verifying — flagged for follow-up rather than pinning to an unverified SHA (would risk red CI). - INF-3: align CLAUDE.md §6's :dev wording with the implemented merged-PR-into-dev trigger (doc alignment; no behavior change). - INF-2: document the fork-PR :dev publish limitation in publish.yml as an accepted trade-off; the push-based alternative is a §6 locked-decision change left for a deliberate call. - INF-4: document the trivy-server root exception (upstream image ships no non-root USER; mitigations noted), per the audit's accepted alternative. - INF-5: add a small tmpfs:[/run] to docker-socket-proxy (HAProxy needs a writable /run under read_only), with a live-verify note. Deviation logged in docs/PLAN.md. * feat(scanners,docs): P3 audit remediation — wire dead Settings knobs + README truth Addresses the P3 tier of docs/reviews/full-audit-2026-07-05.md §10 (feature gaps that mislead users): - FEAT-6 (QUA-3): apply the stored Grype ignore config at scan time — a new grype_policy module materializes the YAML into tmpfs and the worker passes it to Grype via a `-c` config flag (mirroring the Trivy policy path). - FEAT-7 (QUA-3): the New Scan form prefills its severity filter and ignore-unfixed toggle from GET /settings/scanners so instance defaults take effect instead of being overridden by hardcoded form values. - FEAT-4 (QUA-3): the maintenance tick honors auto_update_db + interval, running `trivy image --download-db-only` and `grype db update` best-effort when due. - DOC-1/2/5 + FEAT-1/2/3/8: README aligned with reality — Docker Hub publishing is in scope; uploaded image-tar, Docker-env multi-select scan, and filesystem-archive upload are marked not-implemented; VEX/.trivyignore are global (not per-scan); the ECR/GCR/ACR helper-binaries caveat is stated. - FEAT-5/FEAT-10: offline DB import and admin bulk secret re-encryption are listed as not-yet-implemented; the key-rotation README claim is corrected. New tests cover the Grype config flag/materialization and the DB-update tick. Deviation logged in docs/PLAN.md. * fix(frontend): P4 audit remediation — session expiry, UTC times, restore label, RBAC gating Addresses the P4 tier of docs/reviews/full-audit-2026-07-05.md §10 (frontend correctness / UX): - FE-1: the API client emits an auth-invalidated event on any 401 and AuthContext drops to the login screen, instead of leaving a stale authenticated shell whose every action fails. - FE-3: a shared lib/dates.ts (parseUtc/formatWhen) renders backend naive-UTC timestamps; Account/Backups/Scheduled-scans stop showing UTC as local, and the ScanDetail/Scans private helpers are de-duplicated onto it. - FE-4: BackupsPanel's restore file uses useState (not useRef) so the selected file name re-renders on the destructive restore flow. - FE-5: ScheduledScansPanel constrains the scanner Select by target type (SCANNERS_FOR matrix + auto-correct) and gates Add/Run/Delete behind an operator/admin check; the /settings route is now guarded (viewers → /). Verified with tsc, ESLint, Prettier, and a clean vite build (no frontend test runner yet — FE-10 deferred to P5). Deviation logged in docs/PLAN.md. * fix(frontend): track src/lib/dates.ts (was hidden by the Python lib/ gitignore) The shared date helper added for FE-3 lives under frontend/src/lib/, which the generic Python-oriented `lib/` rule in .gitignore silently excluded — so the file was never committed and CI's fresh checkout failed the frontend build (and the image build) with "Cannot find module '../../lib/dates'". Add a .gitignore exception for the frontend source lib directory and commit the file. * fix(backup,tests): P5 audit remediation — envelope KDF params, migration drift test, deviation log Addresses the P5 tier of docs/reviews/full-audit-2026-07-05.md §10 (maintainability, process, long tail): - item (g): backup restore derives the passphrase key from the bundle's advertised scrypt params (kdf.n/r/p) instead of the module constants, so a bundle written under a different (e.g. older) work factor still restores. derive_key / passphrase_cipher take explicit, validated n/r/p; restore passes the recorded values. - QUA-23: new tests/test_migrations.py runs the real Alembic chain to head against a throwaway DB and asserts the tables/columns match Base.metadata (the rest of the suite builds via create_all). alembic/env.py now respects a caller-provided URL. Deviation-logging debt recorded in docs/PLAN.md (required regardless of fix): FE-2 (hand-rolled API client), INF-10 (HIGH/CRITICAL dogfood floor), API-12 (created_at vs started_at index), FEAT-4 (DB-schedule actuation). QUA-4/QUA-9, QUA-16, and FE-10 are explicitly deferred with rationale. * docs: defer INF-2 explicitly until the repo goes public Per user decision (2026-07-05): keep the merged-PR-only :dev publish trigger for now — while the repo is private, fork-based contributions can't happen, so the fork-secrets gap can't be triggered. Record in docs/PLAN.md that INF-2 must be revisited specifically before the repo is made public, since that is the event that enables fork PRs (and therefore the actual bug). INF-3's CLAUDE.md wording stays matched to the current trigger.
Add guidance for landing multi-PR stacked batches: retarget each child PR's base to the true target branch immediately after its parent merges, re-state the full merge procedure before each merge rather than once per batch, and verify the target branch's actual content after the batch is reported complete instead of assuming merge order alone propagates changes through the stack.
) * ci: batch dev image to a nightly GHCR build; trim per-PR CI minutes Restructure dev-image publishing and cut CI-minute usage. - Add .github/workflows/dev-nightly.yml: build the dev branch multi-arch once nightly (04:00 UTC) + manual dispatch and push the moving ghcr.io/iamgroot60/scrye:dev via the built-in GITHUB_TOKEN. Scheduled runs skip when dev has no new commits in 24h. - publish.yml is now release-only: drop the merged-PR :dev job and its pull_request trigger. Docker Hub (<dockerhub-user>/scrye) is referenced only by the release path. - Split registries by role: Docker Hub for tagged releases, GHCR for dev. - Resolve audit INF-2: a schedule trigger is not PR-triggered, so the fork-withheld-secrets gap no longer applies. - ci.yml minute reduction: run the two informational scanner reports on pushes to main only (dev PRs keep just the gate scans); add a cache-scope input to the build-image action and partition the GHA cache (amd64-ci vs multiarch vs dev-multiarch) so amd64-only and multi-arch builds stop evicting each other. - Update CLAUDE.md §6, docs/PLAN.md §0.6 + Deviations, README, and CONTRIBUTING to the two-registry model; remove the obsolete dev-publish smoke-test doc. * docs: prefer read-only default Actions permissions with per-workflow packages:write An explicit permissions: block overrides the repo-level "Workflow permissions" default, so GHCR push does not require raising the repo-wide default to read/write. Recommend keeping the restrictive read-only default and letting dev-nightly.yml declare its own contents:read + packages:write, matching the least-privilege posture. Update docs/PLAN.md and CONTRIBUTING.md accordingly.
…unts, parallel scanner downloads (#34) The image CI work was dominated by the multi-arch build check, whose arm64 leg runs the whole Dockerfile under QEMU emulation. CI logs showed it rebuilding cold every run (0 cached layers): the deliberately-partitioned `type=gha` cache scope it uses (`multiarch`) is only written on rare main/release events, so its entries age out between runs and the emulated arm64 layers get re-executed from scratch instead of restored. Apply the fixes consistently across all four build paths without weakening the supply-chain posture (scanner checksum verification, digest-pinned bases, and the non-root hardened final stage are unchanged): - Cross-seed the GHA cache scopes. Each build path still WRITES exactly one scope (keeping the 10 GB budget partitioning), but now also READS the frequently-warm sibling scope. The shared build-image action gains an `extra-cache-scopes` input (cache-from = primary + extras, cache-to = primary only). image-multiarch and the release build read the nightly's warm `dev-multiarch`; the nightly reads `multiarch`; the amd64 dogfood build reads `dev-multiarch` for warm amd64 layers. - Persist pip/npm download caches with BuildKit cache mounts so an unchanged dependency isn't re-fetched when its install layer rebuilds; drop PIP_NO_CACHE_DIR (the cache lives in the mount, not the image layer). - Parallelize the trivy/grype/syft download+verify+extract pipelines (each in a background subshell joined by `wait`); a checksum mismatch in any still fails the build via `wait` under `set -e`. Documented in docs/PLAN.md § Build performance (with the do-not-undo invariants and a per-path before/after) and a guardrail rule in CLAUDE.md.
…ions bumps (#33) Reconciles dev with main after the dev->main promotion (#32, squash-merged) and the Dependabot github-actions group bump (#33) that landed directly on main. The promotion squash re-introduced main's older copy of already-promoted dev work, which conflicts with dev's newer versions (the #34 build-performance changes); all such conflicts are resolved in favour of dev. The only content this merge actually brings into dev is #33's action version bumps (checkout v7, setup-python v6, setup-node v6, setup-buildx v4, build-push v7, login v4). After this merge main is an ancestor of dev, so dev no longer shows as behind.
…ev (#35) The dev/main release model (squash-merged promotion PRs, plus Dependabot targeting the default branch main) leaves dev showing commits 'behind' main after each release. Two coupled changes stop that recurring: - .github/dependabot.yml: set target-branch to dev so github-actions bumps open against the integration branch instead of landing on main and never reaching dev. Removes the avoidable drift source. - CLAUDE.md and CONTRIBUTING.md: document a back-merge step — after each dev->main promotion (and after any commit that lands on main directly), merge main into dev, resolving squash-divergence conflicts in favour of dev. Handles the unavoidable promotion-squash case. See docs/PLAN.md § Deviations (2026-07-07) for the rationale and the one-time reconciliation performed alongside this change.
…rge process (#36) (#37) * ci: publish Scrye image to Docker Hub (tagged releases + dev continuous build) (#18) * ci: publish scrye image to Docker Hub on tagged releases and dev pushes Add .github/workflows/publish.yml with two independent publishing paths: - semver tags (v*.*.*) whose commit is on main build the multi-arch (amd64/arm64) image and push <dockerhub-user>/scrye:<version> + :latest - pushes to dev push the moving <dockerhub-user>/scrye:dev continuous-build tag Extract the multi-arch build (QEMU + Buildx + build-push against docker/Dockerfile) into a reusable .github/actions/build-image composite action and refactor ci.yml's multi-arch build-check to consume it, so the build is defined in one place. Publishing uses the DOCKERHUB_USERNAME/ DOCKERHUB_TOKEN repo secrets; ci.yml still never publishes. Update CLAUDE.md and docs/PLAN.md (locked decision 0.6, §9.1, §13, Deviations) and add a CONTRIBUTING.md Releasing section describing both paths. * ci: gate multi-arch build-check to main pushes and PRs to main The image-multiarch job's arm64 leg builds the whole Dockerfile under QEMU emulation, which on a cold type=gha cache takes hours. Only main-scoped runs reliably restore a warm arm64 cache; dev-based PRs rebuild from scratch every time. Gate the check to main pushes and PRs whose base is main. Multi-arch buildability stays proven for dev by publish.yml (builds amd64+arm64 on every dev push and release tag), and dev PRs still run the fast amd64-only image build + dogfood self-scan, so no coverage is lost. * ci(publish): scope :dev publish to merged PRs into dev The :dev Docker Hub publish was triggered by on: push: branches: [dev], which fired on any commit reaching the dev ref — including conflict- resolution pushes to an open, unmerged promotion PR. Re-scope it to on: pull_request: types: [closed] with base dev, gated on pull_request.merged == true, and build the merged commit (merge_commit_sha). The :dev tag now publishes only when a PR is actually merged into dev. The tagged-release path (v*.*.* on main) is unchanged. Sync docs/PLAN.md (§0.6 + Deviations entry) and the CONTRIBUTING.md Releasing section to describe the merged-PR trigger. * test: add throwaway marker to smoke-test the :dev publish on merge to dev (#21) * docs: add full-repository audit report (2026-07-05) (#22) Comprehensive report-only audit covering infrastructure/deployment, backend security, scanner orchestration, API/data/performance, frontend, feature completeness against docs/PLAN.md, and the previously-logged known limitations. Findings are stably ID'd with file:line references, CONFIRMED/PLAUSIBLE confidence markers, failure scenarios, and fix directions, plus a prioritized action list. * fix(security,backup): P0 audit remediation — token minting, restore, webhook URLs (#23) Addresses the P0 tier of docs/reviews/full-audit-2026-07-05.md §10: - QUA-1: cap API-token minting against the caller's effective (token-capped) role, not the owner account's role, so a low-privilege token belonging to an admin can no longer mint an admin token. Adds a regression test. - API-2: run database restore (scrypt + full-DB rebuild) in a threadpool so /healthz stays responsive and the container can't be killed mid-restore. - API-3: chunked executemany restore inserts; yield_per streaming build; drop the redundant bundle re-parse; log + document the in-memory size ceiling. - API-10: raw-artifact files don't travel in a bundle, so exclude the artifacts table from the dump and clear it on restore (no dangling file references). - API-11: refuse restore (409) while a scan is queued or running. - SEC-1: treat a generic webhook URL as a write-only credential (encrypted, masked on read) like Discord; frontend renders it as a password field. Deviation logged in docs/PLAN.md; README backup section updated. * Land P1–P5 audit remediation on dev (P0 already merged via #23) (#29) * perf(worker,api): P1 audit remediation — off-loop heavy work + bounded memory Addresses the P1 tier of docs/reviews/full-audit-2026-07-05.md §10 (availability and performance under real data volume): - API-5: off-load the scan worker's result persistence (10k+ findings flush + raw-JSON write) to a thread via anyio.to_thread, completing the systemic "no synchronous heavy work on the event loop" fix begun in P0 (API-2/3). - SCN-1: cap captured scanner stdout (SCRYE_SCANNER_MAX_OUTPUT_BYTES, default 512 MiB); output past the budget kills the child and fails the scan instead of buffering unbounded JSON. - API-4: read SBOM/backup uploads through read_upload_capped so an oversized body is rejected by reported size / chunked read, never fully buffered first. - API-7: dashboard/metrics load only needed columns per target (load_only) and serve from a short process-wide TTL cache, cleared on app startup and in tests. - API-1: eager-load scan tags (selectinload) in the two list endpoints. - API-15/API-6: run the maintenance tick (schedules + retention) off the event loop and batch retention deletes into one DELETE ... WHERE id IN (...). New tests cover the output cap and upload cap; the dashboard cache TTL/reset is tested. Deviation logged in docs/PLAN.md; .env.example regenerated. * ci,fix(config,compose): P2 audit remediation — supply chain + deployment hardening Addresses the P2 tier of docs/reviews/full-audit-2026-07-05.md §10: - SCN-3: parse the documented comma-separated env form for cors_origins and filesystem_scan_roots (NoDecode + a before-validator), so the filesystem-scan enable switch (SCRYE_FILESYSTEM_SCAN_ROOTS=/path) no longer fails at startup. Adds env-parsing tests. - INF-1: add .github/dependabot.yml (github-actions ecosystem, weekly, grouped). SHA-pinning each `uses:` needs current action SHAs, which this environment's egress policy blocks from resolving/verifying — flagged for follow-up rather than pinning to an unverified SHA (would risk red CI). - INF-3: align CLAUDE.md §6's :dev wording with the implemented merged-PR-into-dev trigger (doc alignment; no behavior change). - INF-2: document the fork-PR :dev publish limitation in publish.yml as an accepted trade-off; the push-based alternative is a §6 locked-decision change left for a deliberate call. - INF-4: document the trivy-server root exception (upstream image ships no non-root USER; mitigations noted), per the audit's accepted alternative. - INF-5: add a small tmpfs:[/run] to docker-socket-proxy (HAProxy needs a writable /run under read_only), with a live-verify note. Deviation logged in docs/PLAN.md. * feat(scanners,docs): P3 audit remediation — wire dead Settings knobs + README truth Addresses the P3 tier of docs/reviews/full-audit-2026-07-05.md §10 (feature gaps that mislead users): - FEAT-6 (QUA-3): apply the stored Grype ignore config at scan time — a new grype_policy module materializes the YAML into tmpfs and the worker passes it to Grype via a `-c` config flag (mirroring the Trivy policy path). - FEAT-7 (QUA-3): the New Scan form prefills its severity filter and ignore-unfixed toggle from GET /settings/scanners so instance defaults take effect instead of being overridden by hardcoded form values. - FEAT-4 (QUA-3): the maintenance tick honors auto_update_db + interval, running `trivy image --download-db-only` and `grype db update` best-effort when due. - DOC-1/2/5 + FEAT-1/2/3/8: README aligned with reality — Docker Hub publishing is in scope; uploaded image-tar, Docker-env multi-select scan, and filesystem-archive upload are marked not-implemented; VEX/.trivyignore are global (not per-scan); the ECR/GCR/ACR helper-binaries caveat is stated. - FEAT-5/FEAT-10: offline DB import and admin bulk secret re-encryption are listed as not-yet-implemented; the key-rotation README claim is corrected. New tests cover the Grype config flag/materialization and the DB-update tick. Deviation logged in docs/PLAN.md. * fix(frontend): P4 audit remediation — session expiry, UTC times, restore label, RBAC gating Addresses the P4 tier of docs/reviews/full-audit-2026-07-05.md §10 (frontend correctness / UX): - FE-1: the API client emits an auth-invalidated event on any 401 and AuthContext drops to the login screen, instead of leaving a stale authenticated shell whose every action fails. - FE-3: a shared lib/dates.ts (parseUtc/formatWhen) renders backend naive-UTC timestamps; Account/Backups/Scheduled-scans stop showing UTC as local, and the ScanDetail/Scans private helpers are de-duplicated onto it. - FE-4: BackupsPanel's restore file uses useState (not useRef) so the selected file name re-renders on the destructive restore flow. - FE-5: ScheduledScansPanel constrains the scanner Select by target type (SCANNERS_FOR matrix + auto-correct) and gates Add/Run/Delete behind an operator/admin check; the /settings route is now guarded (viewers → /). Verified with tsc, ESLint, Prettier, and a clean vite build (no frontend test runner yet — FE-10 deferred to P5). Deviation logged in docs/PLAN.md. * fix(frontend): track src/lib/dates.ts (was hidden by the Python lib/ gitignore) The shared date helper added for FE-3 lives under frontend/src/lib/, which the generic Python-oriented `lib/` rule in .gitignore silently excluded — so the file was never committed and CI's fresh checkout failed the frontend build (and the image build) with "Cannot find module '../../lib/dates'". Add a .gitignore exception for the frontend source lib directory and commit the file. * fix(backup,tests): P5 audit remediation — envelope KDF params, migration drift test, deviation log Addresses the P5 tier of docs/reviews/full-audit-2026-07-05.md §10 (maintainability, process, long tail): - item (g): backup restore derives the passphrase key from the bundle's advertised scrypt params (kdf.n/r/p) instead of the module constants, so a bundle written under a different (e.g. older) work factor still restores. derive_key / passphrase_cipher take explicit, validated n/r/p; restore passes the recorded values. - QUA-23: new tests/test_migrations.py runs the real Alembic chain to head against a throwaway DB and asserts the tables/columns match Base.metadata (the rest of the suite builds via create_all). alembic/env.py now respects a caller-provided URL. Deviation-logging debt recorded in docs/PLAN.md (required regardless of fix): FE-2 (hand-rolled API client), INF-10 (HIGH/CRITICAL dogfood floor), API-12 (created_at vs started_at index), FEAT-4 (DB-schedule actuation). QUA-4/QUA-9, QUA-16, and FE-10 are explicitly deferred with rationale. * docs: defer INF-2 explicitly until the repo goes public Per user decision (2026-07-05): keep the merged-PR-only :dev publish trigger for now — while the repo is private, fork-based contributions can't happen, so the fork-secrets gap can't be triggered. Record in docs/PLAN.md that INF-2 must be revisited specifically before the repo is made public, since that is the event that enables fork PRs (and therefore the actual bug). INF-3's CLAUDE.md wording stays matched to the current trigger. * docs: add stacked-PR landing rules to CLAUDE.md (#30) Add guidance for landing multi-PR stacked batches: retarget each child PR's base to the true target branch immediately after its parent merges, re-state the full merge procedure before each merge rather than once per batch, and verify the target branch's actual content after the batch is reported complete instead of assuming merge order alone propagates changes through the stack. * ci: batch dev image to a nightly GHCR build; trim per-PR CI minutes (#31) * ci: batch dev image to a nightly GHCR build; trim per-PR CI minutes Restructure dev-image publishing and cut CI-minute usage. - Add .github/workflows/dev-nightly.yml: build the dev branch multi-arch once nightly (04:00 UTC) + manual dispatch and push the moving ghcr.io/iamgroot60/scrye:dev via the built-in GITHUB_TOKEN. Scheduled runs skip when dev has no new commits in 24h. - publish.yml is now release-only: drop the merged-PR :dev job and its pull_request trigger. Docker Hub (<dockerhub-user>/scrye) is referenced only by the release path. - Split registries by role: Docker Hub for tagged releases, GHCR for dev. - Resolve audit INF-2: a schedule trigger is not PR-triggered, so the fork-withheld-secrets gap no longer applies. - ci.yml minute reduction: run the two informational scanner reports on pushes to main only (dev PRs keep just the gate scans); add a cache-scope input to the build-image action and partition the GHA cache (amd64-ci vs multiarch vs dev-multiarch) so amd64-only and multi-arch builds stop evicting each other. - Update CLAUDE.md §6, docs/PLAN.md §0.6 + Deviations, README, and CONTRIBUTING to the two-registry model; remove the obsolete dev-publish smoke-test doc. * docs: prefer read-only default Actions permissions with per-workflow packages:write An explicit permissions: block overrides the repo-level "Workflow permissions" default, so GHCR push does not require raising the repo-wide default to read/write. Recommend keeping the restrictive read-only default and letting dev-nightly.yml declare its own contents:read + packages:write, matching the least-privilege posture. Update docs/PLAN.md and CONTRIBUTING.md accordingly. * perf(docker): speed up image builds via cache cross-seeding, cache mounts, parallel scanner downloads (#34) The image CI work was dominated by the multi-arch build check, whose arm64 leg runs the whole Dockerfile under QEMU emulation. CI logs showed it rebuilding cold every run (0 cached layers): the deliberately-partitioned `type=gha` cache scope it uses (`multiarch`) is only written on rare main/release events, so its entries age out between runs and the emulated arm64 layers get re-executed from scratch instead of restored. Apply the fixes consistently across all four build paths without weakening the supply-chain posture (scanner checksum verification, digest-pinned bases, and the non-root hardened final stage are unchanged): - Cross-seed the GHA cache scopes. Each build path still WRITES exactly one scope (keeping the 10 GB budget partitioning), but now also READS the frequently-warm sibling scope. The shared build-image action gains an `extra-cache-scopes` input (cache-from = primary + extras, cache-to = primary only). image-multiarch and the release build read the nightly's warm `dev-multiarch`; the nightly reads `multiarch`; the amd64 dogfood build reads `dev-multiarch` for warm amd64 layers. - Persist pip/npm download caches with BuildKit cache mounts so an unchanged dependency isn't re-fetched when its install layer rebuilds; drop PIP_NO_CACHE_DIR (the cache lives in the mount, not the image layer). - Parallelize the trivy/grype/syft download+verify+extract pipelines (each in a background subshell joined by `wait`); a checksum mismatch in any still fails the build via `wait` under `set -e`. Documented in docs/PLAN.md § Build performance (with the do-not-undo invariants and a per-path before/after) and a guardrail rule in CLAUDE.md. * docs,ci: add post-promotion back-merge step; retarget Dependabot to dev (#35) The dev/main release model (squash-merged promotion PRs, plus Dependabot targeting the default branch main) leaves dev showing commits 'behind' main after each release. Two coupled changes stop that recurring: - .github/dependabot.yml: set target-branch to dev so github-actions bumps open against the integration branch instead of landing on main and never reaching dev. Removes the avoidable drift source. - CLAUDE.md and CONTRIBUTING.md: document a back-merge step — after each dev->main promotion (and after any commit that lands on main directly), merge main into dev, resolving squash-divergence conflicts in favour of dev. Handles the unavoidable promotion-squash case. See docs/PLAN.md § Deviations (2026-07-07) for the rationale and the one-time reconciliation performed alongside this change.
…#38) * docs: overhaul README/CONTRIBUTING; split PLAN into ARCHIVE + ROADMAP Rewrite the documentation to match the current codebase and separate the historical build record from forward-looking planning. - README.md: rewritten from the verified codebase. Deeper Docker deployment section (prerequisites, master-key generation, Docker Hub vs GHCR vs local image, compose invocation, first-run admin bootstrap, persistent-data layout, optional sidecars, reverse-proxy setup, first-run troubleshooting incl. the read-only-root /cache cache-path class of issue). Completed the env-var table (adds SCRYE_FORWARDED_ALLOW_IPS, SCRYE_SCANNER_MAX_OUTPUT_BYTES, SCRYE_SCANNER_CACHE_DIR). Added a UI-based "Configuring OIDC" section. Security model, backup/restore, and monitoring refreshed. Roadmap now links to docs/ROADMAP.md (forward) and docs/ARCHIVE.md (history). - CONTRIBUTING.md: merged the two duplicate "Releasing" sections into one with subsections; updated the docs/ tree and deviation-log pointer to ARCHIVE.md/ROADMAP.md; dropped the now-historical phase/PX branch guidance. - docs/PLAN.md -> docs/ARCHIVE.md: preserved verbatim as the historical build record (phase order, locked decisions, deviations log, build-performance notes); header updated to describe its archival role. - docs/ROADMAP.md: new forward-looking roadmap (near/medium/longer-term) plus known limitations and accepted trade-offs (SBOM content-identity, OIDC/MFA policy scope, key-rotation re-encryption tool, offline DB import, etc.). - CLAUDE.md: repointed docs/PLAN.md references to docs/ARCHIVE.md and noted docs/ROADMAP.md for forward-looking work. * docs,ci: go public and consolidate publishing to GHCR-only Scrye's repository is going public; drop Docker Hub entirely and publish everything to GHCR, and update the docs for a public audience. Publishing (GHCR-only): - publish.yml now builds release tags to ghcr.io/iamgroot60/scrye:<version> and :latest, authenticating with the built-in GITHUB_TOKEN (packages:write) instead of the DOCKERHUB_USERNAME/DOCKERHUB_TOKEN secrets. Adds a canonical-repo guard so a fork that pushes a tag skips instead of failing. - The nightly :dev build (dev-nightly.yml) was already GHCR/GITHUB_TOKEN; only its comments changed. ci.yml, the composite build action, and dependabot.yml had their Docker-Hub-era comments corrected. - INF-2 (the fork-PR :dev secrets gap, previously deferred because the repo was private) is now fully closed: both publish paths are triggered outside pull_request and use GITHUB_TOKEN, so no pull_request-triggered workflow carries a registry secret. The DOCKERHUB_* repo secrets are now unused. Public-repo governance: - Add .github/CODEOWNERS (owner review) and SECURITY.md (private vulnerability reporting, supported-tags table, scope). - Branch protection and signed-commit enforcement are repository settings, not files; tracked as a checklist in docs/ROADMAP.md. Docs: - README: GHCR-only distribution; a complete standalone pull-from-GHCR docker-compose.yml so deploying needs no clone (cloning is now a separate "build from source" path); env vars categorized by necessity (required/conditional/optional); real nginx/Caddy/Traefik reverse-proxy examples; clearer optional-sidecar necessity; GHCR + CI badges; dropped the build-history pointer from the Roadmap section. - CONTRIBUTING § Releasing rewritten for GHCR; ROADMAP reflects the public repo (free arm64 runners, governance checklist); CLAUDE.md locked decision §6 rewritten GHCR-only. Dated deviation entry added to docs/ARCHIVE.md.
…39) Three frontend/backend fixes: - Theme: replace Mantine's minty default teal with the Tailwind teal ramp; primary is teal-700 (#0f766e) in light mode and teal-600 (#0d9488) in dark mode, with autoContrast + luminanceThreshold 0.2 so filled controls stay legible. All primary usages clear WCAG AA (light 5.47:1, dark filled 5.61:1, dark text 4.60:1). Applies everywhere color="teal" resolves via primaryShade (wordmark, nav, buttons, badges, loaders, pagination). - Scans: add DELETE /api/scans/{id} (operator role + CSRF, terminal-status only). Removing the scan cascades to its findings, artifact-metadata rows, and tags via the existing ORM/FK cascade; the on-disk artifact directory is removed via a new remove_scan_artifacts() helper. No schema change, so no migration. A confirmation modal + Delete button is added to the scan detail page. Deleted scans stop feeding the dashboard aggregates and drop out of history/diffs. Backend tests cover full cleanup, RBAC, CSRF, and queued/404. - Nav: fix the active-link matching that lit both "Scans" and "New scan" on /scans/new. The active item is now the longest matching nav path (exact or "to/" prefix), so each item highlights only for its own route. See docs/ARCHIVE.md § Deviations for details.
Update all references to the old GitHub username across the repo after the account rename: - GitHub URLs (github.com/IamGroot60/Scrye → github.com/tyler-rich/Scrye) in README, SECURITY.md, and docs. - GHCR image paths (ghcr.io/iamgroot60/scrye → ghcr.io/tyler-rich/scrye) in README, CONTRIBUTING, CLAUDE.md, docs/ARCHIVE.md, and both publish/nightly workflows. - README badges (CI + GHCR container); escape the dash in the shields.io GHCR badge (tyler--rich) so it renders literally. - CODEOWNERS catch-all owner and LICENSE copyright holder. - Git-identity rule in CLAUDE.md, including the noreply email ([email protected]; numeric ID unchanged). - Workflow repo-owner fork guards: github.repository == 'tyler-rich/Scrye' in both dev-nightly.yml (nightly build) and publish.yml (release build). GHCR auth is unchanged: both workflows use github.actor + GITHUB_TOKEN, which are repo-scoped and carry no hardcoded username.
…le) (#43) Docker Scout scan of the published ghcr.io/tyler-rich/scrye:0.1.0 image surfaced ten CVEs, all inside the bundled upstream scanner binaries' embedded Go stdlib / oras-go / moby / sigstore-timestamp-authority modules — none in Scrye's own attack surface. Version-bump-if-available check outcome: Trivy 0.72.0, Grype 0.115.0, and Syft 1.46.0 are each already the latest available upstream release, so no bump resolves any of the findings yet. Left the Dockerfile pins unchanged and logged the CVE -> upstream-fix-version mapping as a tracked limitation in docs/ARCHIVE.md § Deviations, per the existing bundled-binary CVE-tracking pattern. No binary changed, so the image is unaffected and the app version stays 0.1.0 (no 0.1.1 cut).
* Add files via upload * docs: wire up README screenshots Move uploaded screenshots into docs/screenshots/ and replace the README placeholder table with the real dashboard, new-scan, results, and history captures. Drop the now-stale roadmap item.
Cap each screenshot to a fixed width via HTML img tags so the table renders uniformly regardless of each capture's native dimensions.
Crop each screenshot to its actual content, trimming the large blank page background that made captures like New scan and History look tiny next to Results. Render all four at a fixed 260x200 box so the table is visually consistent.
GitHub's markdown CSS forces height:auto on images, so the explicit height attribute from the previous fix had no effect and thumbnails still rendered at different sizes. Pad each screenshot with matching background color to a common canvas height instead, so a single width attribute renders all four uniformly.
The previous fix cropped each screenshot to its content, then padded back up to the tallest capture's height (1241px) — which happened to equal the original canvas size, silently undoing the crop and leaving the thumbnails just as mismatched as before. Crop all four to the same 900px window from the original captures instead, so they share actual pixel dimensions with no padding involved.
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.
Summary
Promotes
devtomain. Changes since the last promotion (#41):CHANGELOG.mdwith the 0.1.0 entryWhat changed
CHANGELOG.md(new)docs/ARCHIVE.md— CVE check notedocs/ROADMAP.md— drop stale roadmap item now that screenshots are wired upREADME.md— real screenshots in place of the placeholder tabledocs/screenshots/*.png(new)