Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
91 changes: 91 additions & 0 deletions .github/workflows/containers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,94 @@ jobs:
node scripts/container-image-size-budget.mjs
--target "${{ matrix.target }}"
--image "spaceapp-ci:${{ matrix.target }}-${{ matrix.arch }}"

- name: Smoke check core container (/readyz)
if: matrix.target == 'core'
run: |
docker network create spaceapp-smoke-${{ matrix.arch }}
docker run -d --name smoke-pg-${{ matrix.arch }} --network spaceapp-smoke-${{ matrix.arch }} \
-e POSTGRES_PASSWORD=smokepassword -e POSTGRES_USER=spaceapp -e POSTGRES_DB=spaceapp \
pgvector/pgvector:0.8.3-pg17-trixie@sha256:3a3147ccd9a0aee952bb69e1acea1eb3cc7183544185b87a662f872cfa47ae3b

ready=0
for _ in $(seq 1 30); do
if docker exec smoke-pg-${{ matrix.arch }} pg_isready -U spaceapp; then ready=1; break; fi
sleep 1
done
[ "$ready" = 1 ] || { echo "PostgreSQL not ready"; exit 1; }

mkdir -p /tmp/smoke-secrets-${{ matrix.arch }}
echo "postgres://spaceapp:smokepassword@smoke-pg-${{ matrix.arch }}:5432/spaceapp" > /tmp/smoke-secrets-${{ matrix.arch }}/database-url
echo "smoke-session-secret-at-least-32-chars-long" > /tmp/smoke-secrets-${{ matrix.arch }}/session-secret
echo "smoke-setup-token" > /tmp/smoke-secrets-${{ matrix.arch }}/setup-token
chmod 0600 /tmp/smoke-secrets-${{ matrix.arch }}/*

docker run -d --name smoke-core-${{ matrix.arch }} --network spaceapp-smoke-${{ matrix.arch }} \
-v /tmp/smoke-secrets-${{ matrix.arch }}:/run/secrets:ro \
-p 4911:4911 \
spaceapp-ci:core-${{ matrix.arch }}

core_ready=0
for _ in $(seq 1 45); do
if curl -sf http://127.0.0.1:4911/readyz | grep -q '"ok":true'; then
echo "Core readyz smoke check passed"
core_ready=1
break
fi
sleep 2
done
[ "$core_ready" = 1 ] || { echo "Core /readyz not ready"; exit 1; }

- name: Cleanup core smoke check resources
if: always() && matrix.target == 'core'
run: |
docker logs smoke-core-${{ matrix.arch }} 2>/dev/null || true
docker stop smoke-core-${{ matrix.arch }} smoke-pg-${{ matrix.arch }} 2>/dev/null || true
docker rm -f smoke-core-${{ matrix.arch }} smoke-pg-${{ matrix.arch }} 2>/dev/null || true
docker network rm spaceapp-smoke-${{ matrix.arch }} 2>/dev/null || true
rm -rf /tmp/smoke-secrets-${{ matrix.arch }}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

- name: Smoke check browser container (Chromium launch)
if: matrix.target == 'browser'
run: |
docker run --rm --entrypoint chromium spaceapp-ci:browser-${{ matrix.arch }} \
--no-sandbox --headless --disable-gpu --dump-dom about:blank

- name: Smoke check cli container (CLI host socket & OpenCode)
if: matrix.target == 'cli'
run: |
docker run --rm --entrypoint opencode spaceapp-ci:cli-${{ matrix.arch }} \
--version

docker run -d --name smoke-cli-${{ matrix.arch }} \
-e SPACE_CLI_HOST_SOCKET=/run/spaceapp-cli/host.sock \
spaceapp-ci:cli-${{ matrix.arch }}

socket_ready=0
for _ in $(seq 1 30); do
if ! docker inspect -f '{{.State.Running}}' smoke-cli-${{ matrix.arch }} 2>/dev/null | grep -q 'true'; then
echo "CLI container terminated unexpectedly"
exit 1
fi
if docker exec smoke-cli-${{ matrix.arch }} test -S /run/spaceapp-cli/host.sock 2>/dev/null; then
socket_ready=1
break
fi
sleep 1
done
[ "$socket_ready" = 1 ] || { echo "CLI socket not created"; exit 1; }

docker exec smoke-cli-${{ matrix.arch }} node -e "
const net = require('node:net');
const client = net.createConnection('/run/spaceapp-cli/host.sock');
client.on('connect', () => { console.log('CLI host socket reachable'); process.exit(0); });
client.on('error', (err) => { console.error('Socket connect failed:', err); process.exit(1); });
setTimeout(() => { console.error('Socket timeout'); process.exit(1); }, 5000);
"

- name: Cleanup cli smoke check resources
if: always() && matrix.target == 'cli'
run: |
docker logs smoke-cli-${{ matrix.arch }} 2>/dev/null || true
docker stop smoke-cli-${{ matrix.arch }} 2>/dev/null || true
docker rm -f smoke-cli-${{ matrix.arch }} 2>/dev/null || true
22 changes: 9 additions & 13 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ COPY . .
RUN npm ci --no-audit --no-fund \
&& npm run build \
&& npm prune --omit=dev \
&& find node_modules -type f \( -name '*.js.map' -o -name '*.mjs.map' -o -name '*.cjs.map' -o -name '*.css.map' \) -delete
&& find node_modules -type f \( -name '*.js.map' -o -name '*.mjs.map' -o -name '*.cjs.map' -o -name '*.css.map' \) -delete \
&& node deploy/docker/prepare-container-targets.mjs

FROM node:22.23.0-trixie-slim@sha256:cfd8f2a5bc50526aee08e88970979f92722828e7dcc6d8983607fb8bff4bdb82 AS runtime-base
ARG TARGETARCH
Expand Down Expand Up @@ -54,17 +55,8 @@ RUN npm install --global --ignore-scripts --no-audit --no-fund "npm@${NPM_VERSIO
&& groupadd --gid 10001 spaceapp \
&& useradd --uid 10001 --gid 10001 --create-home --home-dir /var/lib/spaceapp-cli --shell /bin/bash spaceapp
WORKDIR /app
COPY --from=build --chown=spaceapp:spaceapp /app/package.json /app/package-lock.json ./
COPY --from=build --chown=spaceapp:spaceapp /app/node_modules ./node_modules
COPY --from=build --chown=spaceapp:spaceapp /app/apps ./apps
COPY --from=build --chown=spaceapp:spaceapp /app/packages ./packages
COPY --from=build --chown=spaceapp:spaceapp /app/agent-skills ./agent-skills
COPY --from=build --chown=spaceapp:spaceapp /app/LICENSE /app/NOTICE /app/THIRD_PARTY_NOTICES.md ./
COPY --from=build --chown=spaceapp:spaceapp /app/THIRD_PARTY_LICENSES ./THIRD_PARTY_LICENSES
COPY --from=build --chown=spaceapp:spaceapp /app/scripts/reset-owner-password.mjs ./scripts/reset-owner-password.mjs
COPY --from=build --chown=spaceapp:spaceapp /app/scripts/rotate-owner-setup-token.mjs ./scripts/rotate-owner-setup-token.mjs
COPY --from=build --chown=spaceapp:spaceapp /app/scripts/portable-backup.mjs ./scripts/portable-backup.mjs
COPY --from=build --chown=spaceapp:spaceapp /app/scripts/portable-restore.mjs ./scripts/portable-restore.mjs
COPY --chown=spaceapp:spaceapp deploy/docker ./deploy/docker
RUN chmod 0755 deploy/docker/core-entrypoint.sh deploy/docker/browser-entrypoint.sh deploy/docker/cli-entrypoint.sh \
&& install -m 0755 deploy/docker/public-cli-wrapper.sh /usr/local/bin/spaceapp-cli-wrapper \
Expand All @@ -73,6 +65,11 @@ RUN chmod 0755 deploy/docker/core-entrypoint.sh deploy/docker/browser-entrypoint
done

FROM runtime-base AS core
COPY --from=build --chown=spaceapp:spaceapp /app/targets/core ./
COPY --from=build --chown=spaceapp:spaceapp /app/scripts/reset-owner-password.mjs ./scripts/reset-owner-password.mjs
COPY --from=build --chown=spaceapp:spaceapp /app/scripts/rotate-owner-setup-token.mjs ./scripts/rotate-owner-setup-token.mjs
COPY --from=build --chown=spaceapp:spaceapp /app/scripts/portable-backup.mjs ./scripts/portable-backup.mjs
COPY --from=build --chown=spaceapp:spaceapp /app/scripts/portable-restore.mjs ./scripts/portable-restore.mjs
COPY --chown=spaceapp:spaceapp starter-memory /opt/spaceapp/starter-memory
RUN install -d -o spaceapp -g spaceapp -m 0700 \
/var/lib/spaceapp \
Expand All @@ -85,6 +82,7 @@ HEALTHCHECK --interval=15s --timeout=5s --start-period=45s --retries=5 \
ENTRYPOINT ["/app/deploy/docker/core-entrypoint.sh"]

FROM runtime-base AS browser
COPY --from=build --chown=spaceapp:spaceapp /app/targets/browser ./
RUN apt-get update \
&& apt-get install -y --no-install-recommends chromium fonts-liberation liblzma5 \
&& rm -rf /var/lib/apt/lists/* \
Expand All @@ -97,6 +95,7 @@ ENV SPACE_BROWSER_CHROME_PATH=/usr/bin/chromium \
ENTRYPOINT ["/app/deploy/docker/browser-entrypoint.sh"]

FROM runtime-base AS cli
COPY --from=build --chown=spaceapp:spaceapp /app/targets/cli ./
COPY --chown=root:root starter-memory/AGENTS.md /etc/AGENTS.md
COPY --chown=root:root starter-memory/installation-guide.md /etc/spaceapp-installation-guide.md
RUN chmod 0644 /etc/AGENTS.md
Expand All @@ -117,8 +116,5 @@ RUN apt-get update \
/workspaces \
/run/spaceapp-cli \
&& printf '%s\n' "$TARGETARCH" > /usr/local/share/spaceapp-cli-architecture
# The application's default room project is /opt/spaceapp. CLI sessions run in /etc, as
# /etc/AGENTS.md documents, so that path resolves to /etc in this image. Without it, the
# Getting Started OpenCode pane exits with "chdir(2) failed" before OpenCode starts.
RUN ln -s /etc /opt/spaceapp
ENTRYPOINT ["/app/deploy/docker/cli-entrypoint.sh"]
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ Coordinate multi-step tasks conversationally through the Room Agent or speak nat
### Prerequisites
- **Node.js**: Follow the selected `run-spaceapp` launcher version's `engines` requirement. Development of this monorepo requires **>=22.12 <23**; see [Contributing](CONTRIBUTING.md).
- **Hardware**:
- Minimum (`small` profile): 4 CPU cores, 8 GB-class RAM (at least 7 GiB reported by the operating system), 6.5 GiB free disk.
- `large` profile (adds the managed browser): 8 CPU cores, 15 GiB RAM, 11 GiB free disk.
- Minimum (`small` profile): 4 CPU cores, 8 GB-class RAM (at least 7 GiB reported by the operating system), 5.5 GiB free disk.
- `large` profile (adds the managed browser): 8 CPU cores, 15 GiB RAM, 9.5 GiB free disk.
- **Docker**: If Docker is not installed, the installer automatically installs official Docker Desktop (Windows/macOS) or Docker Engine (Linux).

### Run the Installation Command
Expand Down Expand Up @@ -242,9 +242,9 @@ The launcher selects the largest profile that fits the available CPU, RAM, and f

| Profile | Target System | Allocated Services | Minimum Hardware |
| :--- | :--- | :--- | :--- |
| `small` | Laptops / Small VMs | `spaceapp-core`, `spaceapp-cli`, `postgres` | 4 CPUs, 7 GiB RAM, 6.5 GiB disk |
| `medium` | Standard Dev PC | Small stack + `temporal` (background workers & automated workflows) | 4 CPUs, 11 GiB RAM, 8 GiB disk |
| `large` | Workstations / Servers | Medium stack + `spaceapp-browser` (Chromium browser sessions in a separate service) | 8 CPUs, 15 GiB RAM, 11 GiB disk |
| `small` | Laptops / Small VMs | `spaceapp-core`, `spaceapp-cli`, `postgres` | 4 CPUs, 7 GiB RAM, 5.5 GiB disk |
| `medium` | Standard Dev PC | Small stack + `temporal` (background workers & automated workflows) | 4 CPUs, 11 GiB RAM, 7 GiB disk |
| `large` | Workstations / Servers | Medium stack + `spaceapp-browser` (Chromium browser sessions in a separate service) | 8 CPUs, 15 GiB RAM, 9.5 GiB disk |

---

Expand Down
9 changes: 4 additions & 5 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
"scripts": {
"dev": "SPACE_DEV_LOGIN=true SPACE_SESSION_SECRET=dev-only-change-me tsx watch src/server.ts",
"start": "node dist/server.js",
"build:vault-broker": "esbuild src/vault-broker-main.ts --bundle --platform=node --format=esm --target=node20 --outfile=dist/space-vault-broker.mjs",
"build": "tsc -p tsconfig.json && cp src/databases/sqlite-worker.py dist/databases/sqlite-worker.py && chmod +x dist/databases/sqlite-worker.py",
"check": "tsc -p tsconfig.json --noEmit",
"config:reference": "tsx src/config-reference.ts",
Expand All @@ -18,6 +17,7 @@
"@ai-sdk/gateway": "4.0.87",
"@fastify/compress": "^9.1.0",
"@fastify/cookie": "^11.0.2",
"@fastify/cors": "^11.2.0",
"@fastify/helmet": "^13.0.2",
"@fastify/multipart": "^10.0.0",
"@fastify/rate-limit": "^10.3.0",
Expand All @@ -42,9 +42,8 @@
"mammoth": "^1.12.0",
"mysql2": "^3.24.5",
"nanoid": "^5.1.6",
"node-pty": "^1.1.0",
"pdfjs-dist": "6.3.289",
"sanitize-html": "^2.18.1",
"sanitize-html": "^2.17.7",
"zod": "^4.2.1"
},
"devDependencies": {
Expand All @@ -53,7 +52,7 @@
"@types/ws": "^8.18.1",
"tsx": "^4.21.0",
"typescript": "^5.9.3",
"vitest": "^4.1.12"
"vitest": "^4.1.11"
},
"license": "MIT"
"license": "BUSL-1.1"
}
Loading