diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..750b1f2b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,54 @@ +# Dependencies — always reinstall inside the image +**/node_modules +**/backups + +# Logs and package-manager noise +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +# Git and CI (not needed in the runtime image) +.git +.github + +# Env and secrets — never bake local config into layers +.env +.env.* +!.env.example + +# Build, test, and release artifacts +dist +coverage +.nyc_output +release +*.tsbuildinfo + +# OS and editors +.DS_Store +Thumbs.db +.idea +.vscode +*.swp +*.swo + +# Writable app layers and local runtime state (see repo `.gitignore`) +app/L1 +app/L2 +tmp + +# Server-local temp and auth data (image should start clean) +server/tmp +server/data + +# SQLite and local TLS experiments +*.sqlite +*.sqlite-shm +*.sqlite-wal +*.pem +*.csr + +# Optional local tooling caches +.cache +.eslintcache diff --git a/.github/workflows/container-image.yml b/.github/workflows/container-image.yml new file mode 100644 index 00000000..766665e9 --- /dev/null +++ b/.github/workflows/container-image.yml @@ -0,0 +1,65 @@ +# Build the Space Agent image and push to GitHub Container Registry (ghcr.io). +# Image: ghcr.io// with tags from branch, semver tag, and git SHA. +# +# After the first run, grant read access if needed: Package settings → Manage Actions access, +# or keep packages private and authenticate pulls with a PAT or `GITHUB_TOKEN` where supported. + +name: Container Image + +on: + push: + branches: + - main + tags: + - "v*" + workflow_dispatch: + +permissions: + contents: read + packages: write + +concurrency: + group: container-image-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build-and-push: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Image name (GHCR requires lowercase) + run: echo "IMAGE_NAME=ghcr.io/$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_ENV" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Docker metadata (tags, labels) + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha,prefix= + type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) || startsWith(github.ref, 'refs/tags/v') }} + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/AGENTS.md b/AGENTS.md index b7870f82..e0e269b0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -196,8 +196,7 @@ Project concepts: - the browser authenticates through the server and uses a server-issued `space_session` cookie for protected API, module, and app-file access; when the current login is allowed to auto-restore `userCrypto` on the same browser profile, the browser keeps one encrypted local blob in `localStorage`, and authenticated browser code fetches a session-derived wrapping key from the backend by hashing the current backend `sessionId` with the server-held session secret - when `CUSTOMWARE_GIT_HISTORY` is enabled, writable `L1//` and `L2//` roots are treated as optional per-owner local Git repositories with adaptive-debounced server-side commits and rollback APIs - `USER_FOLDER_SIZE_LIMIT_BYTES` optionally caps each `L2//` folder on disk; app-file mutations are checked against a cached per-user size total and only size-reducing mutations are allowed once a user folder is already over the limit -- runtime file discovery is backed by sharded `file_index` state; startup indexes `L0`, `L1`, and layer roots, auth first touch loads only the target user's auth files, and full `L2/` shards are loaded only on demand for file/module access or active mutations rather than preloaded just because their folders exist on disk -- runtime parameters are defined in `commands/params.yaml`; `node space serve` resolves them in this order: launch arguments, stored `.env` params written by `node space set`, then process environment variables, then schema defaults; `node space supervise` accepts the same runtime parameters, owns the public `HOST` and `PORT`, requires `CUSTOMWARE_PATH`, enables source auto-update by default, and passes the remaining resolved params to private `space serve` children; `WORKERS` controls clustered HTTP worker count for `serve` and `supervise`; `CUSTOMWARE_PATH` is the parent directory for writable backend `L1/` and `L2/` storage when configured and also hosts backend-owned cloud-share archives under `share/spaces/` when that feature is enabled; `CUSTOMWARE_WATCHDOG` defaults to `true` and controls live backend customware watching, config watching, and the periodic reconcile backstop without disabling L0/L1 startup indexing, on-demand L2 loading, or explicit clustered mutation sync; `GIT_BACKEND` defaults to `auto` and may force `native` or `isomorphic` for server-owned Git flows such as local history and Git-backed module operations; `LOGIN_ALLOWED` gates the password-login endpoints and login-shell form, `CLOUD_SHARE_ALLOWED` gates hosted cloud-share uploads, `CLOUD_SHARE_URL` tells browser clients which hosted share receiver to use, and page shells receive only `frontend_exposed` values as injected meta tags +- runtime parameters are defined in `commands/params.yaml`; `node space serve` resolves them in this order: launch arguments, stored `.env` params written by `node space set`, then process environment variables, then schema defaults; `node space supervise` accepts the same runtime parameters, owns the public `HOST` and `PORT`, requires `CUSTOMWARE_PATH`, enables source auto-update by default, and passes the remaining resolved params to private `space serve` children; `WORKERS` controls clustered HTTP worker count for `serve` and `supervise`; `CUSTOMWARE_PATH` is the parent directory for writable backend `L1/` and `L2/` storage when configured and also hosts backend-owned cloud-share archives under `share/spaces/` when that feature is enabled; `CUSTOMWARE_WATCHDOG` defaults to `true` and controls live backend customware watching, config watching, and the periodic reconcile backstop without disabling startup indexing or explicit clustered mutation sync; `GIT_BACKEND` defaults to `auto` and may force `native` or `isomorphic` for server-owned Git flows such as local history and Git-backed module operations; `LOGIN_ALLOWED` gates the password-login endpoints and login-shell form, `CLOUD_SHARE_ALLOWED` gates hosted cloud-share uploads, `CLOUD_SHARE_URL` tells browser clients which hosted share receiver to use, and page shells receive only `frontend_exposed` values as injected meta tags - app file APIs use logical app-rooted paths such as `L2/alice/user.yaml` or `/app/L2/alice/user.yaml`, and supported endpoints may also accept `~` or `~/...` for the authenticated user's `L2//...`; those logical paths do not change when `CUSTOMWARE_PATH` relocates the writable backend roots - non-`/api` and non-`/mod` browser entry routes are served from `server/pages/`; `/login` and `/enter` are public and the protected page shells live behind the router-side session gate - detailed browser-runtime rules live in `/app/AGENTS.md` @@ -230,6 +229,7 @@ Project concepts: - `.vscode/launch.json` provides a `Dev Server (npm run dev)` debugger entry that launches the local dev supervisor and auto-attaches to its spawned `node space serve` child so `server/` breakpoints keep working after watcher restarts - `node space serve` to run the server directly - `node space supervise CUSTOMWARE_PATH=` to run the production-ready zero-downtime auto-update supervisor for source checkouts +- optional root `Dockerfile` plus `docker-entrypoint.sh`: image entrypoint uses `tini`, defaults `HOST`/`PORT`/`CUSTOMWARE_PATH`, persists `CUSTOMWARE_PATH` via `node space set`, optionally seeds `L2/admin` once when that tree is missing, then runs `node space supervise`; set image `CMD` or pass a command to `docker run` to replace the default process (see comments in `docker-entrypoint.sh` for `SPACE_DOCKER_*` bootstrap toggles); root `Justfile` exposes `just docker-build`, `just docker-run`, and `just docker-shell`, with optional env overrides `SPACE_DOCKER_IMAGE`, `SPACE_DOCKER_TAG`, `SPACE_DOCKER_VOLUME`, and `SPACE_DOCKER_PORT`; `.dockerignore` trims the build context for Node/npm layouts; root `docker-compose.yaml` runs `space-agent:local` with a named Docker volume mounted at `/srv/space/customware` (build the image first, e.g. `just docker-build`) - `npm run install:packaging` to install packaging-only dependencies - `npm run desktop:dev`, `npm run desktop:pack`, `npm run desktop:dist`, and `npm run package:desktop:macos:dev` for the Electron host and packaging flow, with the macOS dev build command producing the default unpacked local `.app` bundle - `.github/workflows/release-desktop.yml` builds tagged desktop releases for Windows, macOS, and Linux on both x64 and arm64; automatic tag runs and manual `workflow_dispatch` reruns both require the selected `v*` tag to be on `main` history, and both skip only when a newer `v*` tag is already on `main` after it; every publish updates the GitHub Release for that tag before uploading clobbered artifacts selected by `packaging/release-asset-filters.yaml` with uniform `Space-Agent---.` asset names that collapse a redundant trailing `.0` patch to the project's normal two-segment release version diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..70b5e722 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +# syntax=docker/dockerfile:1 + +FROM node:lts + +ENV HOST=0.0.0.0 +ENV PORT=3000 + +RUN apt-get update \ + && apt-get install -y --no-install-recommends tini \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app +COPY . /app +RUN npm install + +COPY docker-entrypoint.sh / +RUN chmod +x /docker-entrypoint.sh + +ENTRYPOINT ["/usr/bin/tini", "--", "/docker-entrypoint.sh"] +# Omit CMD so the entrypoint default path runs; set CMD or pass `docker run … cmd` to override. diff --git a/Justfile b/Justfile new file mode 100644 index 00000000..a5319746 --- /dev/null +++ b/Justfile @@ -0,0 +1,28 @@ +# Space Agent — local task runner (https://github.com/casey/just) +# Docker: `just docker-build`, `just docker-run`, `just docker-shell` + +# Image ref for `docker build` / `docker run` recipes below. +IMAGE := env_var_or_default("SPACE_DOCKER_IMAGE", "space-agent") +TAG := env_var_or_default("SPACE_DOCKER_TAG", "local") +FULL_IMAGE := IMAGE + ":" + TAG + +# Named volume for writable L1/L2 (matches docker-entrypoint default CUSTOMWARE_PATH). +VOLUME := env_var_or_default("SPACE_DOCKER_VOLUME", "space-agent-customware") + +# Host port published to container PORT (default 3000 in Dockerfile). +PORT := env_var_or_default("SPACE_DOCKER_PORT", "3000") + +default: + @just --list + +# Build the image from the repo root (honors `.dockerignore`). +docker-build: + docker build -t {{FULL_IMAGE}} . + +# Run the default supervised stack: publish HOST:PORT → container :3000 and persist customware. +docker-run: + docker run --rm -p {{PORT}}:3000 -v {{VOLUME}}:/srv/space/customware {{FULL_IMAGE}} + +# Interactive shell in the image (skips entrypoint bootstrap; no server started). +docker-shell: + docker run --rm -it -e SPACE_DOCKER_SKIP_INIT=1 -v {{VOLUME}}:/srv/space/customware {{FULL_IMAGE}} bash diff --git a/README.md b/README.md index ec541b69..a8a7e7f3 100644 --- a/README.md +++ b/README.md @@ -1,93 +1,31 @@ -

- Space Agent banner -

- -

-
- Try Live Now! -
-
- Run local App - Host yourself -

- -

Created by Agent Zero.

- -

- Discord -   - X -   - YouTube -   - Ask DeepWiki -

- -

- Watch Space Agent on YouTube -

+[![Space Agent banner](./.github/readme-banner-thin.svg)](https://space-agent.ai) + +[![Try Live Now!](./.github/readme-try-live-now.svg)](https://space-agent.ai) + +[![Run local App](https://img.shields.io/badge/Run%20local%20App-59F0A8?style=for-the-badge&labelColor=07111F&color=59F0A8)](https://github.com/agent0ai/space-agent/releases/latest) +[![Host yourself](https://img.shields.io/badge/Host%20yourself-FFFFFF?style=for-the-badge&labelColor=07111F&color=FFFFFF)](#a-real-server-for-you-or-your-team) + +[![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/qVnMIg?referralCode=HhsRaZ&utm_medium=integration&utm_source=template&utm_campaign=generic) + +### Created by [Agent Zero](https://agent-zero.ai). + +[![Discord](https://img.shields.io/badge/Discord-5865F2?style=flat&logo=discord&logoColor=white)](https://discord.gg/B8KZKNsPpj) +[![X](https://img.shields.io/badge/X-000000?style=flat&logo=x&logoColor=white)](https://x.com/Agent0ai) +[![YouTube](https://img.shields.io/badge/YouTube-FF0000?style=flat&logo=youtube&logoColor=white)](https://www.youtube.com/@AgentZeroFW) +[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/agent0ai/space-agent) + +[![Watch Space Agent on YouTube](./.github/thumbnail.webp)](https://www.youtube.com/watch?v=CNRHxEZ8yqs) ## Why Space Agent Is Different - - - - - - - - - - - - - - - - - - - - - - - - - -
- The agent reshapes the interface
- Ask for a page, tool, widget, or workflow and the agent can build it straight into the running workspace while you work. -
- Endless possibilities
- The agent is not trapped inside a fixed product surface. It can develop the capabilities it needs from within the system itself and keep extending the Space toward whatever the user can imagine. -
- Space Agent app icon - - The agent lives in the frontend runtime
- Space Agent runs in the browser layer itself, whether you open it in a tab or through the desktop app, so the agent can work directly with the same framework, modules, spaces, and UI it is reshaping. -
- Text-based agent
- New capabilities can live in simple SKILL.md files that the agent can write and extend itself in plain text. -
- Token-efficient execution
- No bulky tool-call JSON. When action is needed, the agent can stay in plain text and plain JavaScript inside the same message. -
- Puzzle-piece modularity
- The core stays small. Most of Space Agent is made of modular pieces that can be added, removed, or swapped cleanly instead of being welded into one rigid app. -
- Space Agent helmet -
- Personal to hierarchical
- Use Space Agent as a completely personal assistant, or organize it into a hierarchical system of users and groups as the scope grows. -
- Per-user work, group sharing
- Users can build in their own layer without affecting anyone else, then groups can share tools, workflows, and behavior across teams when they are ready. -
- Space Agent astronaut - - Persistent admin and time travel
- When something breaks, admin mode gives you a stable control plane, and Git-backed history lets you roll back user or group changes without taking everyone down with you. -
+| | | +| :--- | :--- | +| **The agent reshapes the interface** — Ask for a page, tool, widget, or workflow and the agent can build it straight into the running workspace while you work. | **Endless possibilities** — The agent is not trapped inside a fixed product surface. It can develop the capabilities it needs from within the system itself and keep extending the Space toward whatever the user can imagine. | +| ![Space Agent app icon](packaging/resources/icons/source/space-agent-icon-256.webp) | **The agent lives in the frontend runtime** — Space Agent runs in the browser layer itself, whether you open it in a tab or through the desktop app, so the agent can work directly with the same framework, modules, spaces, and UI it is reshaping. | +| **Text-based agent** — New capabilities can live in simple `SKILL.md` files that the agent can write and extend itself in plain text. | **Token-efficient execution** — No bulky tool-call JSON. When action is needed, the agent can stay in plain text and plain JavaScript inside the same message. | +| **Puzzle-piece modularity** — The core stays small. Most of Space Agent is made of modular pieces that can be added, removed, or swapped cleanly instead of being welded into one rigid app. | ![Space Agent helmet](app/L0/_all/mod/_core/visual/res/chat/admin/helmet_no_bg_256.webp) | +| **Personal to hierarchical** — Use Space Agent as a completely personal assistant, or organize it into a hierarchical system of users and groups as the scope grows. | **Per-user work, group sharing** — Users can build in their own layer without affecting anyone else, then groups can share tools, workflows, and behavior across teams when they are ready. | +| ![Space Agent astronaut](app/L0/_all/mod/_core/visual/res/engineer/astronaut_red_512h.webp) | **Persistent admin and time travel** — When something breaks, admin mode gives you a stable control plane, and Git-backed history lets you roll back user or group changes without taking everyone down with you. | ## Try it in 30 seconds @@ -103,8 +41,6 @@ Grab the latest build from [GitHub Releases](https://github.com/agent0ai/space-a ### A real server, for you or your team - - ```bash git clone https://github.com/agent0ai/space-agent.git cd space-agent @@ -132,6 +68,16 @@ node space set CUSTOMWARE_PATH=/srv/space/customware node space supervise HOST=0.0.0.0 PORT=3000 # zero downtime auto-update ``` +### Deploying to Railway + +[Railway](https://railway.com) can run Space Agent from this repo using the checked-in [`railway.toml`](./railway.toml): the service builds from the root [`Dockerfile`](./Dockerfile), sets `CUSTOMWARE_PATH` to `/data/customware`, and expects a **persistent volume** at that path so user data survives redeploys (see the `volumes` entry under `[service.experimental]` in `railway.toml`). + +1. Create a Railway project and connect this repository (or push the image you build from the same `Dockerfile`). +2. Attach storage so `/data/customware` is a mounted volume; without it, redeploys can wipe local state. +3. Deploy; Railway injects `PORT` and the container listens on `HOST=0.0.0.0`. + +**Default login (Docker / Railway first boot):** username `admin`, password `change-me-now`. The entrypoint creates this user only when the admin layer is missing on the volume (see [`docker-entrypoint.sh`](./docker-entrypoint.sh)). Change the password immediately after first sign-in, or set `SPACE_DOCKER_ADMIN_PASSWORD` before the first boot if you want a different initial password. + Run `node space help` to see the full command surface and built-in help for each from [`commands/params.yaml`](./commands/params.yaml). ## AI-driven development and documentation diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 00000000..a580d912 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,20 @@ +# Build the image first: `just docker-build` (tag `space-agent:local`). +# Writable L1/L2 state lives in the named volume at the same path as `docker-entrypoint.sh` +# defaults (`CUSTOMWARE_PATH=/srv/space/customware`). +# Agent configs can be mounted as `~/conf/onscreen-agent.yaml` + +services: + space-agent: + build: + context: . + dockerfile: Dockerfile + container_name: space-agent + image: space-agent:local + ports: + - "3000:3000" + volumes: + - space-agent-customware:/data/customware + restart: unless-stopped + +volumes: + space-agent-customware: diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100644 index 00000000..45cd215e --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,117 @@ +#!/usr/bin/env bash +# Space Agent container entrypoint (used with tini as PID 1). +# +# Default (no image CMD / docker run args): persist CUSTOMWARE_PATH, optionally +# seed the admin user once, then exec `node space supervise`. +# +# Custom command: any docker CMD or trailing `docker run ... -- command` is +# exec-replaced as-is (no bootstrap), so operators own env and ordering. +# docker run … bash +# docker run … node space serve HOST=0.0.0.0 PORT=3000 +# +# Env (defaults): +# CUSTOMWARE_PATH Writable L1/L2 root (default: /srv/space/customware) +# HOST, PORT Passed through to supervise/serve via process env / .env +# +# Bootstrap toggles: +# SPACE_DOCKER_SKIP_INIT=1 +# Skip bootstrap entirely; requires a non-empty command (exec "$@"). +# SPACE_DOCKER_SKIP_ADMIN_BOOTSTRAP=1 +# Skip admin user creation only (still runs `node space set CUSTOMWARE_PATH=…`). +# SPACE_DOCKER_BOOTSTRAP_BEFORE_CMD=1 +# When a custom command is present, run bootstrap first, then exec "$@". +# +# Admin seed (only if ${CUSTOMWARE_PATH}/L2/admin does not exist): +# SPACE_DOCKER_ADMIN_PASSWORD (default: change-me-now) +# SPACE_DOCKER_ADMIN_FULL_NAME (default: Admin) + +# Fail fast on errors, treat unset variables as errors, and catch failures in pipelines. +set -euo pipefail + +# App root for `node space …` (WORKDIR in the image is /app; this script lives at /). +cd /app + +# Writable L1/L2 backend root: default for containers; export so child processes see it. +export CUSTOMWARE_PATH="${CUSTOMWARE_PATH:-/data/customware}" +# Support NODE_ENV, RAILWAY_ENVIRONMENT, or default to production +if [[ -n "${NODE_ENV:-}" ]]; then + export NODE_ENV="$NODE_ENV" +elif [[ -n "${RAILWAY_ENVIRONMENT:-}" ]]; then + export NODE_ENV="$RAILWAY_ENVIRONMENT" +else + export NODE_ENV="production" +fi +export HOST="${HOST:-0.0.0.0}" +export PORT="${PORT:-3000}" +export WORKERS="${WORKERS:-1}" + +# Ensure the path exists before user create / supervise touch it. +mkdir -p "${CUSTOMWARE_PATH}" + +# Treat common boolean spellings as true for SPACE_DOCKER_* toggles. +truthy() { + case "${1:-}" in + 1 | true | yes | on) return 0 ;; + *) return 1 ;; + esac +} + +# Persist CUSTOMWARE_PATH into project `.env` so CLI and supervise agree with the container layout. +bootstrap_customware_path() { + node space set "CUSTOMWARE_PATH=${CUSTOMWARE_PATH}" +} + +# One-time admin seed: `space user create` fails if the user already exists, so only run when +# L2/admin is absent (survives container restarts with the same volume). +bootstrap_admin_if_missing() { + # Opt-out of admin only (run_bootstrap still runs bootstrap_customware_path before this runs). + if truthy "${SPACE_DOCKER_SKIP_ADMIN_BOOTSTRAP:-}"; then + return 0 + fi + local admin_dir="${CUSTOMWARE_PATH}/L2/admin" + # Idempotent across restarts: create only when the L2 tree for `admin` is missing. + if [[ -d "${admin_dir}" ]]; then + return 0 + fi + local pw="${SPACE_DOCKER_ADMIN_PASSWORD:-change-me-now}" + local fn="${SPACE_DOCKER_ADMIN_FULL_NAME:-Admin}" + node space user create admin --password "${pw}" --full-name "${fn}" --groups _admin +} + +run_bootstrap() { + # `.env` must list CUSTOMWARE_PATH before user create writes under CUSTOMWARE_PATH/L2/… + bootstrap_customware_path + bootstrap_admin_if_missing +} + +# Operator wants raw control: no `space set`, no admin seed — only exec the given command. +if truthy "${SPACE_DOCKER_SKIP_INIT:-}"; then + if [[ "$#" -eq 0 ]]; then + echo "docker-entrypoint: SPACE_DOCKER_SKIP_INIT is set but no command was given." >&2 + exit 2 + fi + # Hand off PID 1 role to the user command (still under tini). + exec "$@" +fi + +# Image CMD or `docker run … cmd`: replace this shell with the supplied command. +# Optionally run bootstrap first when SPACE_DOCKER_BOOTSTRAP_BEFORE_CMD is set. +if [[ "$#" -gt 0 ]]; then + if truthy "${SPACE_DOCKER_BOOTSTRAP_BEFORE_CMD:-}"; then + run_bootstrap + fi + # Custom process becomes the main container workload. + exec "$@" +fi + +# No CMD args: default production path — bootstrap then supervise (HOST/PORT from env / .env). +run_bootstrap +# Supervise stays in the foreground; signals go to the Node process under tini. + +if [[ "${NODE_ENV}" == "development" ]]; then + CMD="node space server" +else + CMD="node space supervise --auto-update-interval 0" +fi + +exec ${CMD} HOST=${HOST} PORT=${PORT} CUSTOMWARE_PATH=${CUSTOMWARE_PATH} WORKERS=${WORKERS} diff --git a/railway.toml b/railway.toml new file mode 100644 index 00000000..455a4d28 --- /dev/null +++ b/railway.toml @@ -0,0 +1,30 @@ +# railway.toml +[projects] +name = "space-agent" + +[[service]] +name= "server" +env = { RAILWAY_VOLUME_MOUNT_PATH = "/data/customware" } +[service.build] +builder = "DOCKERFILE" +dockerfilePath = "Dockerfile" + +[service.deploy] +# start command defaults to the ENTRYPOINT +# preDeployCommand = [""] +healthcheckPath = "/" +healthcheckTimeout = 100 +restartPolicyType = "ON_FAILURE" +restartPolicyMaxRetries = 10 + +# Require a volume for persistent data +#requiredMountPath = "/data/customware" + +[service.variables] +# NOTE: Railway injects PORT automatically. +CUSTOMWARE_PATH = "/data/customware" +#CUSTOMWARE_PATH = $RAILWAY_VOLUME_MOUNT_PATH + +[service.experimental] +# Enable Railway Volumes for persistent file storage +volumes = ["/data/customware"] \ No newline at end of file