diff --git a/.gitignore b/.gitignore index ed1d8f04..2fd3eadb 100644 --- a/.gitignore +++ b/.gitignore @@ -70,3 +70,6 @@ docs/internal/ # Transient temp dir a file-roots test creates under the backend working dir backend/sfr-app-*/ + +# Local-only (was tracked; keep on disk, do not publish) +hardened-pipeline-skeleton/ diff --git a/.pr-body-tmp.md b/.pr-body-tmp.md deleted file mode 100644 index 1fbbefc9..00000000 --- a/.pr-body-tmp.md +++ /dev/null @@ -1,27 +0,0 @@ -## Summary - -- Add a History API sync layer (`useUrlSync`) that maps shell state to `/nodes//...` paths without react-router. -- Gate URL normalization on permissions and license readiness; preserve the current URL on authz read failures (fail closed). -- Add stack-list frozen error UI with retry, transition-specific dirty guards for popstate, and user docs at `docs/features/deep-links.mdx`. -- Fix cold-load/refresh regressions: skip dashboard reset on initial node mount; defer URL writer until hydrated `activeView` matches the route. -- Mobile URL contract: `/dashboard` opens Home (content surface); `/stacks` opens the stack list. List surface always writes `/stacks` even when `activeView` is Fleet or Resources. -- Mobile stack deep links: hydrate `pendingDetailStack`, use `loadFileForRoute` + `routeDetailError` for compose failures (frozen URL, `MobileDetailError` + Retry). -- **QA follow-up (`20b35df7`):** Bootstrap `activeView`/tabs from URL via `readUrlRouteState` so Fleet/Security/Resources cold-load correctly; settle route phase when state already matches (fixes in-app URL writes); normalize unknown view segments; open Monaco editor tabs from `/compose`/`/env`/`/files` deep links. - -## Test plan - -- [x] `cd frontend && npx tsc -b --noEmit` -- [x] Unit: `readUrlRouteState`, `useUrlSync`, `senchoRoute`, `useViewNavigationState` (52+ tests) -- [x] E2E routing spec expanded (shell view content assertions, mobile cases) — re-run on CI / fresh preview -- [ ] Manual QA on rebuilt preview image: - - [ ] Cold load `/nodes/local/fleet`, `/security/images`, `/resources` (correct view, not Dashboard) - - [ ] Dashboard → Fleet → Security updates address bar - - [ ] `/stacks//env` opens Monaco env tab (not Anatomy-only) - - [ ] `/nodes/local/not-a-view` normalizes to dashboard - - [ ] Mobile stack deep link + compose failure retry (from prior commit) - -## Notes - -- Internal architecture docs are local-only (gitignored), not in this PR. -- Anatomy panel sub-tabs remain URL-opaque; only Monaco editor tabs update the path. -- Remote node slug suffix (`name-id`) is existing `nodeSlug` behavior; not changed in this PR. diff --git a/hardened-pipeline-skeleton/.github/workflows/publish-hardened.yml b/hardened-pipeline-skeleton/.github/workflows/publish-hardened.yml deleted file mode 100644 index bb8f0662..00000000 --- a/hardened-pipeline-skeleton/.github/workflows/publish-hardened.yml +++ /dev/null @@ -1,89 +0,0 @@ -# Skeleton only. Do not create Studio-Saelix/sencho-hardened until explicit authorization is given. -name: Publish Hardened Build - -on: - workflow_dispatch: - inputs: - source_ref: - description: Immutable source commit SHA - required: true - type: string - base_image_digest: - description: Immutable base-image digest - required: true - type: string - policy_revision: - description: Immutable scanner and OpenVEX policy revision - required: true - type: string - -permissions: - contents: read - -jobs: - validate-inputs: - runs-on: ubuntu-24.04 - outputs: - source_ref: ${{ steps.inputs.outputs.source_ref }} - base_image_digest: ${{ steps.inputs.outputs.base_image_digest }} - policy_revision: ${{ steps.inputs.outputs.policy_revision }} - steps: - # Replace every placeholder with a verified, full commit SHA before enabling. - - uses: actions/checkout@ - with: - ref: ${{ inputs.source_ref }} - persist-credentials: false - - id: inputs - shell: bash - run: | - [[ "${{ inputs.source_ref }}" =~ ^[0-9a-f]{40}$ ]] - [[ "${{ inputs.base_image_digest }}" =~ ^sha256:[0-9a-f]{64}$ ]] - [[ "${{ inputs.policy_revision }}" =~ ^[0-9a-f]{40}$ ]] - printf 'source_ref=%s\n' "${{ inputs.source_ref }}" >> "$GITHUB_OUTPUT" - printf 'base_image_digest=%s\n' "${{ inputs.base_image_digest }}" >> "$GITHUB_OUTPUT" - printf 'policy_revision=%s\n' "${{ inputs.policy_revision }}" >> "$GITHUB_OUTPUT" - - build-and-assess: - needs: validate-inputs - runs-on: ubuntu-24.04 - permissions: - contents: read - id-token: write - strategy: - fail-fast: false - matrix: - platform: [linux/amd64, linux/arm64] - steps: - - uses: actions/checkout@ - with: - ref: ${{ needs.validate-inputs.outputs.source_ref }} - persist-credentials: false - # TODO: install pinned Buildx and build from the source ref, lockfile, - # base-image digest, and platform. Export an OCI layout for scanning. - - name: Build immutable platform image - run: exit 1 # TODO: replace with a pinned Buildx invocation - # TODO: run a pinned Trivy image scan and evaluate the approved OpenVEX file. - - name: Gate Trivy and OpenVEX findings - run: exit 1 # TODO: fail exploitable critical or high findings - # TODO: generate SPDX and CycloneDX SBOMs plus SLSA provenance. - - name: Generate supply-chain attestations - run: exit 1 # TODO: replace with pinned tooling - # TODO: sign the immutable platform digest with Cosign keyless OIDC. - - name: Sign platform digest - run: exit 1 # TODO: replace with pinned Cosign tooling - - publish-manifest: - needs: build-and-assess - runs-on: ubuntu-24.04 - permissions: - contents: read - packages: write - id-token: write - attestations: write - environment: hardened-production - steps: - # TODO: assemble the approved multi-architecture manifest, attach SBOM - # and provenance, sign it, then publish only this immutable target: - # ghcr.io/studio-saelix/sencho-hardened - - name: Publish approved manifest - run: exit 1 # TODO: replace after explicit authorization diff --git a/hardened-pipeline-skeleton/PUBLISH_POLICY.md b/hardened-pipeline-skeleton/PUBLISH_POLICY.md deleted file mode 100644 index 2c947087..00000000 --- a/hardened-pipeline-skeleton/PUBLISH_POLICY.md +++ /dev/null @@ -1,28 +0,0 @@ -# Hardened Build publish policy - -> Do not create `Studio-Saelix/sencho-hardened` until explicit authorization is given. - -## Publish prerequisites - -1. Build from an approved immutable source revision. -2. Pin all base images by digest and retain the resolved lockfile. -3. Produce the required architecture images before creating the manifest. -4. Scan each architecture image and evaluate findings against the approved OpenVEX policy. -5. Block publication when a known critical or high finding is exploitable and lacks an approved remediation decision. -6. Generate and attach SBOM and provenance records. -7. Sign the immutable manifest digest with Cosign. -8. Publish the matching AGPLv3 corresponding source notice with the release metadata. - -## Advisory and remediation - -An advisory can be recommended, cautionary, blocked, or unavailable. A blocked advisory prevents a new published tag. A newly disclosed issue in an already published image is triaged, recorded in OpenVEX where appropriate, remediated in a new immutable build, and communicated through the advisory channel. - -Rollback repoints the supported tag to a prior signed digest only after confirming that its advisory state is suitable for use. Rollback does not overwrite an existing immutable digest. - -## Evidence retained per publication - -- Source revision, dependency lockfile digest, and base-image digests -- Scanner reports and OpenVEX version -- SBOM and provenance attestations -- Cosign signature verification output -- Publication timestamp, manifest digest, and advisory decision diff --git a/hardened-pipeline-skeleton/README.md b/hardened-pipeline-skeleton/README.md deleted file mode 100644 index 0e9b2feb..00000000 --- a/hardened-pipeline-skeleton/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# Hardened Build publishing skeleton - -> Do not create `Studio-Saelix/sencho-hardened` until explicit authorization is given. - -This local skeleton defines the intended publishing controls for the Admiral-only Hardened Build image channel. It is preparation material, not a publishing system and not a private-source product. - -The Hardened Build image is a separately published container artifact. Sencho remains AGPLv3, and every release must make the corresponding source available under the AGPLv3 terms. - -## Assurance statement - -At publish time, the pipeline must establish that the image has zero known critical or high exploitable CVEs according to the approved scanner policy and OpenVEX assessment. This is a point-in-time statement, not a guarantee that no vulnerability will be discovered later. - -Each published image must include: - -- A multi-architecture manifest and immutable digest -- An SBOM -- Build provenance -- A Cosign signature and verification material -- OpenVEX statements for assessed findings -- A documented CVE remediation and republish process - -## Channel separation - -Community images continue through the public channels. Hardened Build inputs are immutable source revision, lockfile, base-image digest, policy revision, and architecture list. The resulting image is published only to `ghcr.io/studio-saelix/sencho-hardened` after all gates pass. - -The workflow skeleton intentionally contains placeholders instead of live credentials, action revisions, or publishing configuration. Fill them only in an authorized private delivery repository. - -See [PUBLISH_POLICY.md](PUBLISH_POLICY.md) for the release gate and remediation rules. diff --git a/images-tab.md b/images-tab.md deleted file mode 100644 index 04be17ff..00000000 --- a/images-tab.md +++ /dev/null @@ -1,227 +0,0 @@ -- generic [ref=f1e3]: - - generic [ref=f1e4]: - - generic [ref=f1e7]: - - generic [ref=f1e8]: Sencho - - generic [ref=f1e9]: v0.94.1 - - button "Switch node" [ref=f1e11]: - - generic [ref=f1e14] [cursor=pointer]: - - generic [ref=f1e15]: Node · LOCAL - - generic [ref=f1e16]: Local - - generic [ref=f1e20]: - - button "Create Stack" [ref=f1e22] - - button [ref=f1e23] - - button [ref=f1e24] - - generic [ref=f1e25]: - - combobox [expanded] [ref=f1e32] - - generic [ref=f1e33]: - - generic [ref=f1e34]: - - button "All 1" [pressed] [ref=f1e35]: - - text: All - - generic [ref=f1e36]: "1" - - button "Up 1" [ref=f1e37]: - - text: Up - - generic [ref=f1e38]: "1" - - button "Down 0" [ref=f1e39]: - - text: Down - - generic [ref=f1e40]: "0" - - button "Updates 0" [ref=f1e41]: - - text: Updates - - generic [ref=f1e42]: "0" - - button "Hide filters" [ref=f1e43] - - listbox "Suggestions" [ref=f1e49]: - - generic [ref=f1e51]: - - button "UNLABELED 1" [expanded] [ref=f1e52]: - - generic [ref=f1e53]: UNLABELED - - generic [ref=f1e54]: "1" - - option [selected] [ref=f1e59]: - - button "UP bookstack" [ref=f1e60] [cursor=pointer]: - - generic [ref=f1e61]: UP - - generic [ref=f1e62]: bookstack - - button [ref=f1e65] - - button "Live · no stack changes in 1h LIVE · OPEN ACTIVITY →" [ref=f1e66] [cursor=pointer]: - - generic [ref=f1e67]: Live · no stack changes in 1h - - generic [ref=f1e70]: LIVE · OPEN ACTIVITY → - - generic [ref=f1e71]: - - generic [ref=f1e72]: - - navigation "Primary" [ref=f1e73]: - - button "Home" [ref=f1e74] - - button "Fleet" [ref=f1e79] - - button "Resources" [ref=f1e88] - - button "Security" [ref=f1e92] - - button "App Store" [ref=f1e97] - - button "Logs" [ref=f1e103] - - button "Update" [ref=f1e107] - - button "Schedules" [ref=f1e114] - - generic [ref=f1e119]: - - button "Open search (Ctrl+K)" [ref=f1e120] - - button "Theme" [ref=f1e124] - - button "Notifications" [ref=f1e125] - - button "Profile" [ref=f1e126]: BO - - generic [ref=f1e128]: - - generic [ref=f1e132]: - - generic [ref=f1e133]: - - generic [ref=f1e134]: - - generic [ref=f1e135]: Action needed - - generic [ref=f1e137]: - - generic [ref=f1e138]: "2 actions: fixable findings, publicly exposed affected images ·" - - generic [ref=f1e139]: 10 images scanned · scanner ready - - button "Update affected images →" [ref=f1e144] - - generic [ref=f1e145]: - - generic [ref=f1e146]: - - generic [ref=f1e147]: CRITICAL - - generic [ref=f1e148]: "0" - - generic [ref=f1e149]: - - generic [ref=f1e150]: HIGH - - generic [ref=f1e151]: "31" - - generic [ref=f1e152]: - - generic [ref=f1e153]: LAST SCAN - - generic [ref=f1e154]: 23h ago - - generic [ref=f1e155]: - - tablist [ref=f1e157]: - - tab "Overview" [ref=f1e159] - - tab "Images" [active] [selected] [ref=f1e166] - - tab "Compose risks" [ref=f1e178] - - tab "Secrets" [ref=f1e182] - - tab "Policies" [ref=f1e188] - - tab "Suppressions" [ref=f1e193] - - tab "History" [ref=f1e200] - - tab "Scanner setup" [ref=f1e206] - - tabpanel "Images" [ref=f1e314]: - - generic [ref=f1e315]: - - generic [ref=f1e316]: - - textbox "Search images..." [ref=f1e321] - - combobox [ref=f1e323]: - - generic [ref=f1e324]: All severities - - table [ref=f1e333]: - - rowgroup [ref=f1e334]: - - row [ref=f1e335]: - - columnheader [ref=f1e336] [cursor=pointer]: - - button "Image" [ref=f1e337] - - columnheader [ref=f1e338] [cursor=pointer]: - - button "Findings" [ref=f1e339] - - columnheader [ref=f1e340] [cursor=pointer]: - - button "Last scan" [ref=f1e341] - - columnheader [ref=f1e344] [cursor=pointer]: - - button "Severity" [ref=f1e345] - - columnheader "Actions" [ref=f1e346] - - rowgroup [ref=f1e347]: - - row [ref=f1e348]: - - cell [ref=f1e349]: - - button "lscr.io/linuxserver/bookstack:latest" [ref=f1e350] - - cell [ref=f1e351]: - - button "6H14 fixable" [ref=f1e352]: - - generic [ref=f1e353]: 6H - - generic [ref=f1e354]: 14 fixable - - cell "23h ago" [ref=f1e355] - - cell [ref=f1e356]: - - button "HIGH" [ref=f1e357] [cursor=pointer] - - cell [ref=f1e359]: - - button "Scan lscr.io/linuxserver/bookstack:latest" [ref=f1e360] - - row [ref=f1e361]: - - cell [ref=f1e362]: - - button "lscr.io/linuxserver/bookstack@sha256:13dc508fb46aa7d2b726b6c872e4903892bda68391ac948a5603d3ca60f0937e" [ref=f1e363] - - cell [ref=f1e364]: - - button "6H14 fixable" [ref=f1e365]: - - generic [ref=f1e366]: 6H - - generic [ref=f1e367]: 14 fixable - - cell "23h ago" [ref=f1e368] - - cell [ref=f1e369]: - - button "HIGH" [ref=f1e370] [cursor=pointer] - - cell [ref=f1e372]: - - button "Scan lscr.io/linuxserver/bookstack@sha256:13dc508fb46aa7d2b726b6c872e4903892bda68391ac948a5603d3ca60f0937e" [ref=f1e373] - - row [ref=f1e374]: - - cell [ref=f1e375]: - - button "lscr.io/linuxserver/bazarr:latest" [ref=f1e376] - - cell [ref=f1e377]: - - button "2H1 fixable" [ref=f1e378]: - - generic [ref=f1e379]: 2H - - generic [ref=f1e380]: 1 fixable - - cell "23h ago" [ref=f1e381] - - cell [ref=f1e382]: - - button "HIGH" [ref=f1e383] [cursor=pointer] - - cell [ref=f1e385]: - - button "Scan lscr.io/linuxserver/bazarr:latest" [ref=f1e386] - - row [ref=f1e387]: - - cell [ref=f1e388]: - - button "lscr.io/linuxserver/jackett:latest" [ref=f1e389] - - cell [ref=f1e390]: - - button "1H1 fixable" [ref=f1e391]: - - generic [ref=f1e392]: 1H - - generic [ref=f1e393]: 1 fixable - - cell "23h ago" [ref=f1e394] - - cell [ref=f1e395]: - - button "HIGH" [ref=f1e396] [cursor=pointer] - - cell [ref=f1e398]: - - button "Scan lscr.io/linuxserver/jackett:latest" [ref=f1e399] - - row [ref=f1e400]: - - cell [ref=f1e401]: - - button "saelix/sencho:pr-1603" [ref=f1e402] - - cell [ref=f1e403]: - - button "3H4 fixable" [ref=f1e404]: - - generic [ref=f1e405]: 3H - - generic [ref=f1e406]: 4 fixable - - cell "23h ago" [ref=f1e407] - - cell [ref=f1e408]: - - button "HIGH" [ref=f1e409] [cursor=pointer] - - cell [ref=f1e411]: - - button "Scan saelix/sencho:pr-1603" [ref=f1e412] - - row [ref=f1e413]: - - cell [ref=f1e414]: - - button "lscr.io/linuxserver/prowlarr:latest" [ref=f1e415] - - cell [ref=f1e416]: - - button "1H1 fixable" [ref=f1e417]: - - generic [ref=f1e418]: 1H - - generic [ref=f1e419]: 1 fixable - - cell "23h ago" [ref=f1e420] - - cell [ref=f1e421]: - - button "HIGH" [ref=f1e422] [cursor=pointer] - - cell [ref=f1e424]: - - button "Scan lscr.io/linuxserver/prowlarr:latest" [ref=f1e425] - - row [ref=f1e426]: - - cell [ref=f1e427]: - - button "saelix/sencho:pr-1600" [ref=f1e428] - - cell [ref=f1e429]: - - button "3H4 fixable" [ref=f1e430]: - - generic [ref=f1e431]: 3H - - generic [ref=f1e432]: 4 fixable - - cell "23h ago" [ref=f1e433] - - cell [ref=f1e434]: - - button "HIGH" [ref=f1e435] [cursor=pointer] - - cell [ref=f1e437]: - - button "Scan saelix/sencho:pr-1600" [ref=f1e438] - - row [ref=f1e439]: - - cell [ref=f1e440]: - - button "saelix/sencho:pr-1609" [ref=f1e441] - - cell [ref=f1e442]: - - button "3H4 fixable" [ref=f1e443]: - - generic [ref=f1e444]: 3H - - generic [ref=f1e445]: 4 fixable - - cell "23h ago" [ref=f1e446] - - cell [ref=f1e447]: - - button "HIGH" [ref=f1e448] [cursor=pointer] - - cell [ref=f1e450]: - - button "Scan saelix/sencho:pr-1609" [ref=f1e451] - - row [ref=f1e452]: - - cell [ref=f1e453]: - - button "saelix/sencho:pr-1608" [ref=f1e454] - - cell [ref=f1e455]: - - button "3H4 fixable" [ref=f1e456]: - - generic [ref=f1e457]: 3H - - generic [ref=f1e458]: 4 fixable - - cell "23h ago" [ref=f1e459] - - cell [ref=f1e460]: - - button "HIGH" [ref=f1e461] [cursor=pointer] - - cell [ref=f1e463]: - - button "Scan saelix/sencho:pr-1608" [ref=f1e464] - - row [ref=f1e465]: - - cell [ref=f1e466]: - - button "saelix/sencho:pr-1610" [ref=f1e467] - - cell [ref=f1e468]: - - button "3H4 fixable" [ref=f1e469]: - - generic [ref=f1e470]: 3H - - generic [ref=f1e471]: 4 fixable - - cell "23h ago" [ref=f1e472] - - cell [ref=f1e473]: - - button "HIGH" [ref=f1e474] [cursor=pointer] - - cell [ref=f1e476]: - - button "Scan saelix/sencho:pr-1610" [ref=f1e477] \ No newline at end of file