Skip to content

Commit 954c314

Browse files
GiniGini
authored andcommitted
Gate hardened container in CI
1 parent 446f348 commit 954c314

5 files changed

Lines changed: 47 additions & 4 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,40 @@ jobs:
2727
cache: npm
2828
- run: npm ci
2929
- run: npm run check
30+
31+
container:
32+
name: Hardened container smoke
33+
runs-on: ubuntu-latest
34+
timeout-minutes: 15
35+
steps:
36+
- uses: actions/checkout@v4
37+
- name: Build image
38+
run: docker build --tag onevibe:${{ github.sha }} .
39+
- name: Start read-only container
40+
run: |
41+
docker run --detach --init --name onevibe-ci \
42+
--publish 4311:4311 \
43+
--read-only \
44+
--tmpfs /tmp:size=64m,noexec,nosuid \
45+
--tmpfs /var/lib/onevibe:size=64m,uid=10001,gid=10001,mode=700 \
46+
--cap-drop ALL \
47+
--security-opt no-new-privileges:true \
48+
--env ONEVIBE_API_HOST=0.0.0.0 \
49+
--env ONEVIBE_API_PORT=4311 \
50+
--env ONEVIBE_DATA_DIR=/var/lib/onevibe \
51+
onevibe:${{ github.sha }}
52+
- name: Verify health and non-root runtime
53+
run: |
54+
for attempt in $(seq 1 30); do
55+
if curl --fail --silent http://127.0.0.1:4311/api/health > /tmp/onevibe-health.json; then break; fi
56+
sleep 1
57+
done
58+
grep -q '"status":"healthy"' /tmp/onevibe-health.json
59+
test "$(docker exec onevibe-ci id -u)" = "10001"
60+
test "$(docker inspect --format '{{.Config.User}}' onevibe-ci)" = "onevibe"
61+
- name: Collect container logs
62+
if: always()
63+
run: docker logs onevibe-ci 2>&1 || true
64+
- name: Remove container
65+
if: always()
66+
run: docker rm --force onevibe-ci || true

‎AGENTS.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ ONEVibe is an open-source, Manus-like agent workspace built as a thin product la
1717

1818
## Verification
1919

20-
- `npm run check` is the minimum local gate.
20+
- `npm run check` is the minimum local gate. The GitHub workflow also builds the Docker image and runs a non-root, read-only container smoke test; when Docker is available locally, use `docker build --tag onevibe-ci:local .` and the equivalent flags from `.github/workflows/ci.yml` before changing the container contract.
2121
- UI work must be inspected in the browser at desktop and mobile widths.
2222
- A feature is complete only when its real enforcement path is exercised; otherwise call it a demo, adapter, preview, or contract.
2323

@@ -59,7 +59,7 @@ For local Claude E2E, use the protected host-only LiteLLM relay configuration do
5959

6060
The Better Auth foundation is feature-gated by `ONEVIBE_AUTH_ENABLED=true`. It must fail closed when `BETTER_AUTH_SECRET` or the real OTP webhook is missing. In authenticated local mode, newly created data is scoped by the server-derived user ID; legacy ownerless data is inaccessible. Do not enable it in a deployed environment until every data-plane repository has user/org scope, legacy import is explicit, and the production email path is accepted. Never add a fake OTP, browser-returned OTP, or development console OTP to make the login appear complete.
6161

62-
Run `npm run e2e:auth-owner` for the local auth/ownership proof. It uses a loopback mail-catcher to receive the OTP generated by Better Auth, then verifies two real session cookies, unauthenticated `401`, owner-only reads, and cross-user `404` task/project/tag boundaries. The fixture is test delivery infrastructure only; it is not a production email or auth-bypass path.
62+
Run `npm run e2e:auth-owner` for the local auth/ownership proof. It uses a loopback mail-catcher to receive the OTP generated by Better Auth, then verifies two real session cookies, unauthenticated `401`, owner-only inventories, cross-user `404` task/project/project-file/schedule/MCP boundaries, and bounded MCP health ownership. The fixture is test delivery infrastructure only; it is not a production email or auth-bypass path.
6363

6464
The Postgres target contract lives in `server/db/schema.ts` and `server/db/migrations/`; run `npm run db:check` and regenerate only through the reviewed Drizzle config. `npm run db:import -- --dry-run` is the safe first inspection of legacy data; the write path requires `DATABASE_URL`, an existing Better Auth owner, and explicit owner assignment for ownerless records. Do not flip `DATABASE_URL` into production behavior until the TaskStore repository adapter, transaction boundaries, restart/idempotency tests, and deployment migration procedure exist. A passing schema generator or import dry-run is not evidence that the application is using Postgres.
6565

‎TODO.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ Reference: `plan/04-cloud-infrastructure.md`
6666

6767
- [ ] **P4-01** Add auth — feature-gated Better Auth + hashed email-OTP foundation, real delivery webhook, session middleware, login UI, and hardcoded-identity removal are now implemented locally; keep open until production delivery, all route/session acceptance, and Postgres-backed ownership are complete
6868
- [ ] **P4-02** Migrate database — Drizzle/PostgreSQL schema contract, two migrations, owner-required importer, cross-owner relationship validation, disposable live import/restart proof, and a fail-closed persistence-driver guard are now present; keep open until the TaskStore repository adapter, production legacy import, application-level idempotency proof, and a controlled `DATABASE_URL` runtime switch are complete
69-
- [ ] **P4-03** Containerise — current non-root multi-stage `Dockerfile`, hardened SQLite-volume `docker-compose.yml`, and `.env.example` are implemented; keep open until the P4-02 Postgres contract is wired into the image/Compose path rather than shipping an unused database service
69+
- [ ] **P4-03** Containerise — current non-root multi-stage `Dockerfile`, hardened SQLite-volume `docker-compose.yml`, `.env.example`, and a GitHub Actions build/non-root/read-only smoke gate are implemented; keep open until the P4-02 Postgres contract is wired into the image/Compose path rather than shipping an unused database service
7070
- [ ] **P4-04** Deploy to Railway or Fly.io — `railway.toml` or `fly.toml`; deploy instructions in `plan/04-cloud-infrastructure.md#deploy`
7171
- [ ] **P4-05** Add cloud sandbox — integrate **e2b.dev** (`@e2b/code-interpreter`) as the default `sandboxed` execution backend; surface sandbox preview URL in workspace iframe; `E2bRuntimeAdapter` wraps e2b and implements the full `RuntimeAdapter` interface
7272
- [ ] **P4-06** Add multi-tenancy scaffolding — local user ownership now scopes tasks, projects, schedules, conversations, MCP declarations, and task routes; the authenticated two-user HTTP harness now covers cross-user task/project/file/schedule/MCP reads and mutations; keep open until `orgs`/`org_members`, Postgres ownership, migration/import, and exhaustive HTTP negative coverage for every route are complete

‎docs/IMPLEMENTATION-LOG.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Implementation log
22

3+
## 2026-07-17 — enforce the hardened container contract in CI
4+
5+
- Added a separate GitHub Actions container job that builds the multi-stage image, starts it with a read-only root filesystem, no-new-privileges, dropped capabilities, and an ephemeral writable data mount, then verifies `/api/health` and UID 10001.
6+
- Local verification passed with `docker build`, the same security flags, `/api/health`, Compose config validation, and explicit non-root identity checks. The data mount is deliberate: persistence is writable, while the image root remains immutable.
7+
- This improves the P4-03 container release gate but does not claim Postgres runtime support, cloud deployment, or sandbox isolation.
8+
39
## 2026-07-17 — add bounded MCP health probing
410

511
- Added `GET /api/mcp/:id/health`, scoped through the authenticated owner inventory, which starts the declared stdio server with the existing secret-free environment boundary, performs initialization and `tools/list`, and returns only `online`/`offline`, bounded latency, tool count, and generic failure detail.

‎docs/LINEAR-BOARD.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ The runtime registry work is tracked in [ONE-247](https://linear.app/onecomputer
2626

2727
The conversation-branching Phase 2 gap is now tracked in [ONE-250](https://linear.app/onecomputer/issue/ONE-250/bep1-add-durable-conversation-branching-and-edit-message). Commit `66f4d22` implements the local branch endpoint, assistant-ui edit action, immutable source transcript, independent workspace copy, lineage/evidence metadata, and persistence/isolation tests. Cloud authorization, multi-user isolation, sandbox isolation, and live provider acceptance remain open.
2828

29-
The cloud/container foundation is tracked in [ONE-251](https://linear.app/onecomputer/issue/ONE-251/platp1-containerize-onevibe-with-truthful-persistence-and-deployment). The current local slice has a non-root, hardened Docker/Compose runtime with a persistent SQLite volume and health smoke proof. It is intentionally not complete until Postgres/Drizzle, auth/user scoping, managed deployment, and sandbox attestation are wired without decorative or unused services.
29+
The cloud/container foundation is tracked in [ONE-251](https://linear.app/onecomputer/issue/ONE-251/platp1-containerize-onevibe-with-truthful-persistence-and-deployment). The current local slice has a non-root, hardened Docker/Compose runtime with a persistent SQLite volume, health smoke proof, and a GitHub Actions image/non-root/read-only smoke gate. It is intentionally not complete until Postgres/Drizzle, auth/user scoping, managed deployment, and sandbox attestation are wired without decorative or unused services.
3030

3131
The first MCP extension slice is tracked in [ONE-252](https://linear.app/onecomputer/issue/ONE-252/extp1-add-governed-mcp-configuration-and-adapter-injection). The local implementation adds SQLite v6 persistence, audited CRUD, strict command/argument validation, a Computers-view configuration surface, a bounded per-server health/tool-catalog probe, and secret-free injection into tool-capable Claude SDK turns. It is intentionally not production MCP governance: authenticated ownership, secret brokering, external-server health/attestation, and per-organization isolation remain open under P4/P6.
3232

0 commit comments

Comments
 (0)