Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
2cceb57
test: add disposable Linux lifecycle acceptance workflow
tbiyss Sep 7, 2026
044c7c4
docs: correct lifecycle acceptance section placement
tbiyss Sep 7, 2026
a630c89
test: isolate transaction fixtures from installed lifecycle state
tbiyss Sep 7, 2026
ae2cc59
fix: isolate service image provisioning from operator environment
tbiyss Sep 7, 2026
9d35eca
fix: build service images from the service-owned checkout
tbiyss Sep 7, 2026
5f10381
test: exercise real updater rollback with a controlled smoke fixture
tbiyss Sep 7, 2026
94927ff
test: verify actual OS reboot inside a disposable KVM guest
tbiyss Sep 7, 2026
ac0a602
release: prepare follow-up candidate and verify image export
tbiyss Sep 7, 2026
cf175dc
test: strengthen rollback evidence and use IPv4 guest networking
tbiyss Sep 7, 2026
d44c67a
test: support untouched fresh-install settings in rollback fixture
tbiyss Sep 7, 2026
4e8c057
fix: recognize exact reviewed public runtime artifact fixtures
tbiyss Sep 7, 2026
72a1a84
Merge exact public artifact fixture review into release candidate
tbiyss Sep 7, 2026
8aeab6e
docs: record candidate scanner and lifecycle verification scope
tbiyss Sep 7, 2026
ddb6e1c
docs: record verified Linux lifecycle reboot and rollback evidence
tbiyss Sep 7, 2026
bd870e5
Merge verified Linux service lifecycle and rollback fixes
tbiyss Sep 7, 2026
6b3f604
docs: record passed isolated operations release gates
tbiyss Sep 7, 2026
e6957ba
Merge latest mode wording into release candidate
tbiyss Sep 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .github/workflows/linux-lifecycle.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Linux lifecycle

on:
workflow_dispatch:
pull_request:
paths:
- .github/workflows/linux-lifecycle.yml
- scripts/check-linux-lifecycle.sh
- scripts/check-update-rollback-hosted.mjs

permissions:
contents: read

jobs:
lifecycle:
# A hosted VM with real PID-1 systemd: a job container cannot prove service installation.
runs-on: ubuntu-24.04
timeout-minutes: 60
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
fetch-depth: 0
persist-credentials: false
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: "24"
- name: Install rootless runtime prerequisites in the disposable VM
run: |
sudo apt-get update
sudo apt-get install -y podman uidmap slirp4netns fuse-overlayfs
- name: Exercise installation, restart, fixture recovery and uninstall
run: bash scripts/check-linux-lifecycle.sh "$RUNNER_TEMP/lifecycle-evidence"
- name: Upload non-secret lifecycle evidence
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: linux-lifecycle-${{ github.sha }}
path: ${{ runner.temp }}/lifecycle-evidence/
if-no-files-found: error
34 changes: 34 additions & 0 deletions .github/workflows/linux-reboot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Linux guest reboot

on:
workflow_dispatch:
pull_request:
paths:
- .github/workflows/linux-reboot.yml
- scripts/check-linux-reboot.sh
- scripts/install-systemd.sh
- scripts/uninstall-systemd.sh

permissions:
contents: read

jobs:
reboot:
# The hosted runner survives while a separate, disposable Ubuntu guest actually reboots.
# KVM availability is experimental on hosted runners: missing support fails explicitly.
runs-on: ubuntu-24.04
timeout-minutes: 90
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
fetch-depth: 0
persist-credentials: false
- name: Install, reboot and uninstall in a disposable KVM guest
run: bash scripts/check-linux-reboot.sh
- name: Upload sanitized reboot acceptance evidence
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: linux-guest-reboot-${{ github.sha }}
path: ${{ runner.temp }}/reboot-evidence/reboot.log
if-no-files-found: error
6 changes: 6 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ permissions:
jobs:
evidence:
runs-on: ubuntu-latest
defaults:
run:
# Fail if docker save fails, even when the gzip process itself exits successfully.
shell: bash
permissions:
contents: read
id-token: write
Expand All @@ -19,6 +23,7 @@ jobs:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
fetch-depth: 0
persist-credentials: false
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: "22.13"
Expand All @@ -38,6 +43,7 @@ jobs:
docker image inspect channelgate/release-candidate > dist/release/runtime-image.json
docker run --rm -i --network none --read-only --entrypoint node channelgate/release-candidate - < scripts/runtime-model-inventory.mjs > dist/release/runtime-models.json
docker save channelgate/release-candidate | gzip > dist/release/runtime-image.tar.gz
gzip --test dist/release/runtime-image.tar.gz
- name: Inventory installed OS, Python and npm packages in the image
uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0 (pinned)
with:
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,22 @@ product overview.

No changes recorded after the current candidate.

## 0.6.0-rc.2 — 2026-09-08 (release candidate)

Follow-up candidate for the installer findings from disposable Linux acceptance. Full live QA
remains deferred; stable promotion is not approved.

- Provision rootless images with a clean service-account environment and from the installed
application directory, so operator storage settings and an inaccessible invocation folder
cannot break a dedicated-account installation.
- Export the release image with pipeline failure propagation and verify gzip integrity before
generating its checksums and attestations.
- Recognize exact reviewed public vendor fixtures during release-artifact scanning, including
complete self-test/development keys and compiler-adjacent constants. Unknown values still fail,
and repository files and Git history receive no exceptions.
- Add disposable Linux installation, encrypted backup/restore, real updater rollback and actual
OS reboot checks; authenticated engine and chat acceptance remains a separate live gate.

## 0.6.0-rc.1 — 2026-09-08 (release candidate)

Candidate source and build evidence for review. The GitHub Release remains a draft; the planned
Expand Down
20 changes: 20 additions & 0 deletions FEATURES.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,27 @@
# ChannelGate — Features

- Release artifact scanning recognizes only exact SHA-256 fingerprints of reviewed public
toolchain fixtures. PEM exceptions bind the complete key, never a header or first body line;
repository/history scans and unknown or altered artifact credentials remain strict. Public
provenance is recorded with every exception.

A categorized catalog of what's shipped. Cross-linked to `TEST-PLAN.md` checks.

- **Disposable Linux lifecycle evidence:** the manually dispatchable Linux lifecycle workflow
exercises a fresh dedicated-account systemd install with the full rootless Podman image,
HTTP liveness after restart, encrypted fixture backup/restore and non-destructive uninstall.
A local fixture upstream also proves the real CLI updater restores Git and restarts systemd
after a candidate test failure or readiness failure; engine smoke and test/pretest commands
are controlled fixture inputs.
Its script refuses non-hosted or occupied hosts. A separate KVM guest workflow exercises an
actual OS reboot, service autostart and persistent database/container-volume fixtures.
Authenticated engine update smoke and conversation/session acceptance remain separate live
gates. → TEST-PLAN: Disposable Linux lifecycle workflow.
- **Service-account image provisioning** uses the same explicit environment as the systemd
daemon so operator XDG/container storage settings cannot redirect a fresh build into another
user's private Podman store, and runs from the service-owned checkout so an operator-private
invocation directory cannot prevent Podman namespace setup.

- Development acceptance policy: behavior changes include reproducible Claude and Codex
acceptance definitions and clearly separate automated evidence from live operator validation.
Public contributors do not need access to a private QA service. → TEST-PLAN: Development acceptance policy.
Expand Down
96 changes: 96 additions & 0 deletions TEST-PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,85 @@
Cumulative functional + security regression. Extended per slice. Run top-to-bottom for a full
pass. Many checks are manual (require a real Slack workspace + an authenticated `claude` CLI).

## Disposable Linux lifecycle workflow

- [x] Real fresh-install/restart/encrypted fixture backup/restore/uninstall acceptance passed on
[hosted run 34163913150](https://github.com/makeitfutureDev/channelgate/actions/runs/34163913150),
tested merge SHA `5d9d424f41217945c841603622cc3035a4d61def`. This used synthetic SQL/config data
and a public fixture passphrase in a new dedicated identity, never production data or keys.
The fixture backup was taken while its daemon ran; the replacement restore removed stale
sidecars/stray config and preserved the exact markers with SQLite integrity `ok`.
- [x] Full lifecycle plus real CLI updater rollback passed on
[hosted run 34164530300](https://github.com/makeitfutureDev/channelgate/actions/runs/34164530300),
tested merge SHA `547ae2454d96fc038e97ae111174cebfb3bcd998`. Both the controlled test failure
and deliberately wrong live revision produced durable `rolled_back` state, restored checkout
and healthy service, preserved recovery-snapshot SQL/config markers, and released the lock.
The original candidate was restored before uninstall. Engine smoke and test/pretest commands
were controlled LOCAL fixture inputs; the CLI updater, Git, npm install/audit/static checks,
systemd restart, snapshot and rollback operations were real. Separate unit regressions: 37/37.
- [x] Actual guest OS reboot/autostart/durable fixture/uninstall acceptance passed on
[KVM run 34164530261](https://github.com/makeitfutureDev/channelgate/actions/runs/34164530261),
tested merge SHA `547ae2454d96fc038e97ae111174cebfb3bcd998`. Both OS boot ID and daemon instance
changed, the service started without manual repair, and SQL/rootless-volume markers survived.
These are engine-independent operations results; authenticated conversation gates below remain
unexecuted. The first guest attempt timed out downloading the browser; the passing attempt used
an IPv4 guest network. No production download timeout or image contents were changed.

- Automated setup: dispatch `.github/workflows/linux-lifecycle.yml` for the candidate ref (a PR
changing this workflow/script also runs it). GitHub-hosted Ubuntu 24.04, Node 24, real PID-1
systemd and rootless Podman; no job container, provider credentials or chat connection.
`scripts/check-linux-lifecycle.sh` refuses non-hosted runners, occupied fixture paths, service
units and accounts before mutation. It operates only on `/opt/channelgate-lifecycle` and the
newly installed `/var/lib/channelgate-lifecycle` service identity; never an operator deployment.
The installer image build must succeed even when the invoking runner has its own container
storage configuration; the service account must use its own HOME/store and explicit environment.
Invoke the installer by absolute path while cwd is an operator-private directory; the image
probe/build must run from the service-owned checkout rather than inherit that inaccessible cwd.
- Pass evidence: `linux-lifecycle-<sha>` artifact records source revision, VM image, versions and
every PASS line. Require successful fresh install/image build, non-root container with zero
effective capabilities/no-new-privileges, enabled active service, distinct healthy instance ID
after real systemd restart, encrypted snapshot while the fixture daemon runs, restored
`before-backup` SQL/config markers, SQLite integrity `ok`, removal of stale WAL/SHM and stray
config, healthy restart and uninstall preserving the account/database/encrypted backup.
These checks are engine-independent because they issue no engine turn. Update transaction
unit evidence is uploaded separately as `update-fixture-tests.tap`; injected failures do not
count as real authenticated engine update/rollback acceptance.
- Hosted updater operations: `scripts/check-update-rollback-hosted.mjs` is restricted to that
exact disposable checkout/runtime, controls the LOCAL fixture's engine smoke and test commands,
and creates a local bare Git upstream. It invokes the unmodified CLI updater first against a
candidate whose test exits 42, then one whose live health reports an incorrect revision.
Require two durable `rolled_back` results, checkout/running revision A, new healthy service
instance, released update lock, private operator recovery snapshot and unchanged SQL marker.
The readiness failure must name the deliberately wrong live revision, not just any timeout;
both recovery-snapshot SQL/config markers must match and snapshot integrity must be `ok`.
Finally restore the original tested source revision and verify its fresh healthy instance.
Public evidence must name the smoke stub; no Claude/Codex authentication is proven here.
- Guest OS reboot: dispatch `.github/workflows/linux-reboot.yml` for the candidate ref. The
hosted runner must expose KVM; unavailable acceleration fails explicitly. The wrapper verifies
the official Ubuntu Noble image checksum, boots a separate cloud-init guest, installs Node 24
and the real service/full image, writes `persisted-through-os-reboot` into SQLite and a rootless
named volume, and reboots the GUEST while the runner stays alive. Pass only with different OS
boot ID and daemon instance ID, enabled active service without manual post-boot start, usable
rootless runtime, intact SQL/volume markers, SQLite integrity `ok` and uninstall preserving data.
Artifact `linux-guest-reboot-<sha>` contains sanitized logs only. No keys, disk images, runtime
config or databases are uploaded. This operations case is engine-independent; it does not prove
a resumed Claude/Codex conversation.
- [ ] Post-reboot conversation gate (both configured engines): on a separate disposable Linux VM, install
the candidate and create a channel that writes `LIFECYCLE-BEFORE-REBOOT` in its own work folder.
Record instance ID and engine/session identity, reboot the machine, then ask each engine in
its existing thread to read the marker. Require automatic service start without manual repair,
a new daemon instance ID, preserved marker/session and container-only engine execution.
Engine-free guest reboot evidence cannot clear this conversation/session gate.
- [ ] Authenticated update rollback gate (Claude and Codex): on that disposable deployment,
configure both engine credentials in its own service identity and a local fixture upstream.
Baseline revision A must answer the fixed update smoke response for both engines. Create
fast-forward candidate B with an intentional failing test; invoke `npm run update`.
Require a visible candidate failure, durable `rolled_back` state, restored A checkout and
an operator recovery snapshot of database/config (runtime data is not automatically rolled
back), a new healthy A instance, passing smoke for both baseline engines and a new
ordinary turn in each existing channel. Repeat with a candidate that passes tests but fails
readiness after restart. Never perform induced-failure checks on a production deployment.

## Base modes and independent options (2026-09-08)

- Automated: `modes`, `channel-settings-modal`, `mode-command-audit`, `folders-settings`,
Expand Down Expand Up @@ -4285,3 +4364,20 @@ acceptance gates; no production restart or external message was performed by the
both files successfully and returned the exact marker CG_PERSONAL_REFERENCE_OK_7319. The
fixture was removed. This verifies catalog/reference readability in an existing container;
the deployed author-grant, resume/revocation and Slack cases above remain unexecuted.


## Reviewed public fixtures in release artifacts (2026-09-08)

- Engine-independent automated acceptance: run `node --test test/release-artifact-fixtures.test.js
test/release-secret-history.test.js`. Disposable repositories use synthetic token/PEM fixtures
with a private test copy of the exception catalog. Pass when exact fixtures are waived only
for generated artifacts; tracked files and history still reject them; an unknown token,
incomplete key or same-prefix/different-body key fails with values redacted; gzip stream
boundaries preserve complete-key matching and report each finding once. No engine participates.
- Candidate gate: run the exact-tag Release evidence workflow. Download the actual image archive,
inventory, model hashes and checksums; verify provenance and inspect the scan's reviewed-fixture
count. All unrecognized findings must fail the job. The checked-in catalog records public
source/binary URLs and fingerprints; adding an exception requires identifying its purpose and
verifying exact upstream bytes, not merely finding the value on the internet. Do not exempt
entire paths, packages, credential patterns or repository/history scans.
- Candidate execution remains pending; this regression does not waive the separate live QA campaign.
2 changes: 1 addition & 1 deletion docs/COMPATIBILITY.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Compatibility matrix

Candidate: **0.6.0-rc.1**, prepared 2026-09-08. Stable promotion awaits full live QA.
Candidate: **0.6.0-rc.2**, prepared 2026-09-08. Stable promotion awaits full live QA.
The component matrix below describes this candidate.

## What the GitHub checks prove
Expand Down
8 changes: 8 additions & 0 deletions docs/MAINTAINER-RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ It is not evidence of a built image or signed provenance. No local run should be
successful release workflow. Review upstream runtime licenses and model notices for the actual
candidate before distributing its image.

Generated-artifact secret scanning has a narrow reviewed-public-fixture catalog in
`scripts/reviewed-artifact-fixtures.json`. Each entry binds an exact matched value to public
upstream evidence and an identified purpose; private keys require the complete PEM fingerprint.
Repository/history scans never apply those exceptions. On a new finding, keep the gate failed
until provenance and purpose are verified; add no directory, package or pattern-wide exclusions.
Review the new image's count and digests after a toolchain update rather than carrying an
unexplained match forward. The catalog contains hashes and public evidence links, never values.

## Repository presentation

Repository maintainers can edit the About description, homepage and topics with GitHub settings
Expand Down
2 changes: 1 addition & 1 deletion docs/RELEASE-ACCEPTANCE.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Release acceptance packet

Status: **prepared; full live QA deferred to the planned campaign** (owner instruction,
2026-09-08). Candidate: **0.6.0-rc.1**. These are reproducible definitions, not claimed
2026-09-08). Candidate: **0.6.0-rc.2**. These are reproducible definitions, not claimed
passes. Use disposable private fixtures only. Record the actual channel IDs, host/image revision,
engine/model versions, timestamps and evidence links when executing. No live chat/provider fixture
was created or used during the source remediation. A separate disposable container lifecycle test
Expand Down
Loading
Loading