# SecurityOS — one-command deploy. # # docker compose up -d # then open http://localhost:8088 # # Two services: the web desktop and a Tor daemon. The web container's privacy # proxy (/api/proxy) routes every fetch through Tor over SOCKS5h (DNS + .onion # resolved AT Tor), so the in-OS Browser and Tor Browser exit over Tor by default. # No host networking, no manual flags. Both run read-only with RAM-only scratch # and no Docker container logs, so `docker compose down` leaves no app-state # residue. Reverse-proxy access-log policy remains the operator's responsibility. # # For the full stack that adds the memory-safe Rust proxy sidecar, use # `docker compose -f deploy/docker-compose.yml up -d` instead. For a single # container with no compose at all, see deploy/standalone/ (docker run only). services: tor: build: context: deploy dockerfile: tor/Dockerfile restart: unless-stopped init: true # No caps needed for a SOCKS client on a high port; immutable rootfs; Tor's # DataDirectory is RAM-only (uid 100/gid 101), so circuits/keys never touch disk. security_opt: [no-new-privileges:true] cap_drop: [ALL] read_only: true tmpfs: - /var/lib/tor:uid=100,gid=101,mode=0700 - /tmp mem_limit: 192m pids_limit: 256 logging: { driver: "none" } networks: [securenet] web: build: context: . dockerfile: Dockerfile args: NEXT_PUBLIC_ENABLE_CLOUDMACS: ${NEXT_PUBLIC_ENABLE_CLOUDMACS:-false} container_name: securityos restart: unless-stopped init: true # Wait until Tor has actually finished bootstrapping (the image's healthcheck # checks status/bootstrap-phase, not just the open port), so the first onion # request can't fail on a cold start. depends_on: tor: condition: service_healthy environment: NODE_ENV: production NEXT_TELEMETRY_DISABLED: "1" TMPDIR: /tmp # Route every fetch through Tor's SOCKS5h port. Tor is ON by default; comment # this out for a non-Tor deployment (the proxy then only strips framing). TOR_PROXY: socks5h://tor:9050 security_opt: [no-new-privileges:true] cap_drop: [ALL] read_only: true tmpfs: - /tmp - /SecurityOS/.next/cache - /home/node/.cache mem_limit: 1g pids_limit: 512 logging: { driver: "none" } ports: - "8088:3000" networks: [securenet] # Optional Cloudmacs profile — full Emacs in the browser. It is excluded from # the default stack and from the IONOS production deployment. To run it in an # explicitly authorized local environment, use: # NEXT_PUBLIC_ENABLE_CLOUDMACS=true docker compose --profile cloudmacs \ # up -d --build web cloudmacs # Gotty serves `emacsclient --tty` as a web TTY. Bound to loopback only. # asEnvUser runs Emacs as uid/gid 1000 so the mounted host dirs (owned by the # host user, uid 1000) are writable. Config persists to ~/.cloudmacs.d, files to # ~/cloudmacs-data. The build context is vendored in deploy/cloudmacs/. cloudmacs: profiles: ["cloudmacs"] build: context: deploy/cloudmacs dockerfile: Dockerfile container_name: securityos-cloudmacs restart: unless-stopped init: true stdin_open: true tty: true environment: UNAME: emacs GNAME: emacs EMACS_UID: "1000" EMACS_GID: "1000" UHOME: /home/emacs TERM: xterm-256color volumes: # .emacs.d = the Spacemacs checkout; .spacemacs.d/init.el = the dotfile that # enables the configured layers plus Org and EWW integration. - ${HOME}/.cloudmacs.d:/home/emacs/.emacs.d - ${HOME}/.spacemacs.d:/home/emacs/.spacemacs.d - ${HOME}/cloudmacs-data:/home/emacs/data mem_limit: 2g pids_limit: 1024 ports: - "127.0.0.1:8090:8080" networks: [securenet] networks: securenet: driver: bridge