Monorepo for Aesthetic.Computer
aesthetic.computer
1// ═══════════════════════════════════════════════════════════════════════════════
2// AESTHETIC COMPUTER DEVCONTAINER
3// ═══════════════════════════════════════════════════════════════════════════════
4// This devcontainer powers a creative coding platform with:
5// - Real-time collaborative pieces (WebRTC/WebSocket)
6// - KidLisp interpreter (custom language for generative art)
7// - Emacs daemon for terminal multiplexing (artery-tui)
8// - Multi-service architecture (dev server, session server, edge services)
9//
10// Restored incrementally to isolate VS Code crash causes on Linux/Fedora host.
11// See: .devcontainer/devcontainer.json.backup for original full config
12// ═══════════════════════════════════════════════════════════════════════════════
13{
14 "name": "Aesthetic Computer",
15
16 // ─────────────────────────────────────────────────────────────────────────────
17 // BUILD CONFIGURATION
18 // ─────────────────────────────────────────────────────────────────────────────
19 // Uses a custom Fedora-based Dockerfile (~13.6GB) with:
20 // - Node.js (fnm), Deno, Python 3.11, Rust, SBCL (Common Lisp)
21 // - Emacs-nox, Fish shell, Docker CLI
22 // - Tezos tools (SmartPy, PyTezos) for NFT minting
23 // - ffmpeg, imagemagick for media processing
24 "build": {
25 "dockerfile": "Dockerfile"
26 },
27
28 "remoteUser": "me",
29 "containerUser": "me",
30 "updateRemoteUserUID": true,
31
32 // Override default workspace mount to add SELinux :z label (required on Fedora)
33 "workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/aesthetic-computer,type=bind,consistency=cached",
34 "workspaceFolder": "/workspaces/aesthetic-computer",
35
36 // ─────────────────────────────────────────────────────────────────────────────
37 // VOLUME MOUNTS
38 // ─────────────────────────────────────────────────────────────────────────────
39 // These persist data across container rebuilds and share host resources:
40 //
41 // .emacs.d: Emacs configuration (init.el, packages, themes)
42 // Persisted so emacs daemon doesn't re-download packages on rebuild
43 // Contains artery-tui terminal layout config
44 //
45 // envs: Environment/secrets directory (API keys, credentials)
46 // Sourced by entry.fish, kept separate from git-tracked config
47 //
48 // aesthetic-mkcert-ca: mkcert CA root key and certificate
49 // Persisted so the same CA survives rebuilds and the
50 // Windows/macOS host trust store stays valid
51 //
52 // aesthetic-fish-data: Fish shell history, completions cache
53 // Named volume so shell history persists across rebuilds
54 //
55 // aesthetic-mail-data: Local Maildir cache for mu4e/mbsync (~/.mail)
56 // Named volume so synced mail survives rebuilds
57 //
58 // aesthetic-copilot-data: Claude/Copilot CLI session data
59 // Persists auth tokens and conversation history
60 //
61 // .claude: Claude Code session data and configuration
62 // Persists conversation history, memory, and settings
63 //
64 // .codex: Codex CLI session data and configuration
65 // Persists local thread history and auth/state across rebuilds
66 //
67 // VS Code globalStorage/workspaceStorage: extension state and SecretStorage
68 // backing databases for remote server
69 //
70 // keyrings: libsecret keyring files used by VS Code extension secrets
71 //
72 // docker.sock: Host Docker socket for docker-in-docker
73 // Allows running `docker build` inside container
74 // Two mounts: one for VS Code's Docker extension, one for CLI
75 "mounts": [
76 "source=${localWorkspaceFolder}/.devcontainer/.emacs.d,target=/home/me/.emacs.d,type=bind,consistency=cached",
77 "source=${localWorkspaceFolder}/.devcontainer/envs,target=/home/me/envs,type=bind,consistency=cached",
78 "source=${localEnv:HOME}/.claude,target=/home/me/.claude,type=bind,consistency=cached",
79 "source=${localEnv:HOME}/.claude.json,target=/home/me/.claude.json,type=bind,consistency=cached",
80 "source=${localEnv:HOME}/.codex,target=/home/me/.codex,type=bind,consistency=cached",
81 "source=aesthetic-vscode-globalstorage,target=/home/me/.config/Code/User/globalStorage,type=volume",
82 "source=aesthetic-vscode-workspacestorage,target=/home/me/.config/Code/User/workspaceStorage,type=volume",
83 "source=aesthetic-vscode-keyrings,target=/home/me/.local/share/keyrings,type=volume",
84 "source=aesthetic-mkcert-ca,target=/home/me/.local/share/mkcert,type=volume",
85 "source=aesthetic-fish-data,target=/home/me/.local/share/fish,type=volume",
86 "source=aesthetic-mail-data,target=/home/me/.mail,type=volume",
87 "source=aesthetic-mail-jas-data,target=/home/me/.mail-jas,type=volume",
88 "source=aesthetic-copilot-data,target=/home/me/.copilot,type=volume",
89 "source=/var/run/docker.sock,target=/var/run/docker-host.sock,type=bind",
90 "type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock"
91 ],
92
93 // ─────────────────────────────────────────────────────────────────────────────
94 // PORT FORWARDING
95 // ─────────────────────────────────────────────────────────────────────────────
96 // VS Code automatically forwards these ports from container to host:
97 //
98 // 8888: Main Aesthetic Computer dev server (Vite + Netlify functions)
99 // 8889: Session server (Jamsocket) for multiplayer state sync
100 // 8111: Caddy reverse proxy (SSL termination for local HTTPS)
101 // 8083-8085: Chat system endpoints (system, SOTCE, clock)
102 // 4040: ngrok inspection UI (when tunnel is active)
103 // 12345: Misc dev/debug port
104 // 3478: TURN server for WebRTC NAT traversal (UDP)
105 // 10000-10007: WebRTC media ports for peer connections
106 // 7890: Devcontainer status dashboard
107 // 5555: VS Code extension views dev server (process-tree 3D viz)
108 "forwardPorts": [8888, 8111, 8112, 8889, 8083, 8084, 8085, 4040, 12345, 3478, 7890, 5555, 4177, 3003, 9090, 10000, 10001, 10002, 10003, 10004, 10005, 10006, 10007],
109
110 "portsAttributes": {
111 "8888": {
112 "label": "Aesthetic Computer",
113 "onAutoForward": "notify",
114 "visibility": "public"
115 },
116 "3478": {
117 "label": "TURN Server",
118 "protocol": "udp",
119 "onAutoForward": "silent"
120 },
121 "8111": { "label": "Caddy", "onAutoForward": "silent", "visibility": "public" },
122 "8889": { "label": "Session Server", "onAutoForward": "silent", "visibility": "public" },
123 "8083": { "label": "Chat System", "onAutoForward": "silent", "visibility": "public" },
124 "8084": { "label": "Chat SOTCE", "onAutoForward": "silent", "visibility": "public" },
125 "8085": { "label": "Chat Clock", "onAutoForward": "silent", "visibility": "public" },
126 "5555": { "label": "Extension Views Dev", "onAutoForward": "silent" },
127 "4177": { "label": "AT User Pages Dev", "onAutoForward": "notify", "visibility": "public" },
128 "3003": { "label": "Silo", "onAutoForward": "silent" },
129 "9090": { "label": "Cockpit", "onAutoForward": "silent" }
130 },
131
132 // ─────────────────────────────────────────────────────────────────────────────
133 // CONTAINER ENVIRONMENT VARIABLES
134 // ─────────────────────────────────────────────────────────────────────────────
135 // DISPLAY: X11 forwarding for clipboard sharing with host
136 // Requires `xhost +local:docker` on Linux host
137 // Used by: xclip/xsel for copy-paste in emacs terminals
138 //
139 // HOST_IP: Machine's LAN IP for WebRTC TURN server announcements
140 // Multiplayer pieces need this for peer discovery
141 //
142 // WSL_DISTRO_NAME: Detect WSL environment for path translation
143 //
144 // DOCKER_BUILDKIT: Enable BuildKit for faster container builds
145 // Used when building nested containers (rare)
146 "containerEnv": {
147 "TZ": "America/Los_Angeles",
148 "DISPLAY": "${localEnv:DISPLAY}",
149 "HOST_IP": "${localEnv:HOST_IP}",
150 "WSL_DISTRO_NAME": "${localEnv:WSL_DISTRO_NAME}",
151 "DOCKER_BUILDKIT": "1",
152 "COMPOSE_DOCKER_CLI_BUILD": "1",
153 "CODEX_HOME": "/home/me/.codex",
154 "AGENT_MEMORY_KEY": "${localEnv:AGENT_MEMORY_KEY}",
155 "AGENT_DEVICE_ID": "${localEnv:AGENT_DEVICE_ID}",
156 "AGENT_SESSION_ID": "${localEnv:AGENT_SESSION_ID}",
157 "AGENT_MEMORY_PROJECT": "${localEnv:AGENT_MEMORY_PROJECT}",
158 "AGENT_MEMORY_PROVIDER": "${localEnv:AGENT_MEMORY_PROVIDER}",
159 "AGENT_MEMORY_TICKET": "${localEnv:AGENT_MEMORY_TICKET}",
160 "AGENT_MEMORY_REMOTE_ENABLED": "${localEnv:AGENT_MEMORY_REMOTE_ENABLED}",
161 "AGENT_MEMORY_REMOTE_URL": "${localEnv:AGENT_MEMORY_REMOTE_URL}",
162 "AGENT_MEMORY_REMOTE_TOKEN": "${localEnv:AGENT_MEMORY_REMOTE_TOKEN}",
163 "AGENT_MEMORY_MONGODB_CONNECTION_STRING": "${localEnv:AGENT_MEMORY_MONGODB_CONNECTION_STRING}",
164 "AGENT_MEMORY_MONGODB_NAME": "${localEnv:AGENT_MEMORY_MONGODB_NAME}",
165 "AGENT_MEMORY_INGEST_TOKEN": "${localEnv:AGENT_MEMORY_INGEST_TOKEN}"
166 },
167
168 // ─────────────────────────────────────────────────────────────────────────────
169 // VS CODE CUSTOMIZATIONS
170 // ─────────────────────────────────────────────────────────────────────────────
171 "customizations": {
172 "vscode": {
173 // Extensions auto-installed in container - kept empty to prevent crashes
174 // Extensions run on UI side via remote.extensionKind settings
175 "settings": {
176 // ═══════════════════════════════════════════════════════════════════════
177 // CRITICAL: FILE WATCHER EXCLUSIONS
178 // ═══════════════════════════════════════════════════════════════════════
179 // The workspace has 86 top-level directories and massive folders like:
180 // - kidlisp-n64 (19GB N64 SDK)
181 // - feed/ (archived content)
182 // - packed/, archive/, experiments/
183 //
184 // Without exclusions, VS Code's file watcher hits 99% CPU scanning
185 // millions of files, causing UI freezes and eventual crashes.
186 //
187 // Setting "**": true disables ALL file watching - aggressive but stable.
188 // Trade-off: Must manually refresh explorer, no auto-reload on file change
189 "files.watcherExclude": {
190 "**/node_modules/**": true,
191 "**/kidlisp-n64/**": true,
192 "**/kidlisp-gameboy/**": true,
193 "**/kidlisp-playdate/**": true,
194 "**/feed/**": true,
195 "**/packed/**": true,
196 "**/archive/**": true,
197 "**/experiments/**": true,
198 "**/builds/**": true,
199 "**/target/**": true,
200 "**/.git/objects/**": true,
201 "**/.git/subtree-cache/**": true,
202 "**/dump.rdb": true
203 },
204
205 // ═══════════════════════════════════════════════════════════════════════
206 // GIT CONFIGURATION
207 // ═══════════════════════════════════════════════════════════════════════
208 // Disable git auto-refresh to reduce background I/O
209 // With 86 directories, git status polling compounds watcher issues
210 "git.autorefresh": false,
211
212 // ═══════════════════════════════════════════════════════════════════════
213 // COPILOT EXTENSION HANDLING
214 // ═══════════════════════════════════════════════════════════════════════
215 // Force Copilot extensions to run on UI (host) side, not in container.
216 // Installing copilot-chat in container causes crashes - see commit aff03abf
217 // This also keeps API keys on host rather than in container
218 // Don't auto-update extensions - prevents mid-session breakage
219 "extensions.autoUpdate": false,
220 "extensions.ignoreRecommendations": true,
221 "settingsSync.keybindingsPerPlatform": false
222 },
223 "extensions": [
224 "mongodb.mongodb-vscode",
225 "Anthropic.claude-code",
226 "ms-python.python",
227 "ms-toolsai.jupyter",
228 "openai.chatgpt"
229 ]
230 }
231 },
232
233 // ─────────────────────────────────────────────────────────────────────────────
234 // DOCKER RUN ARGUMENTS
235 // ─────────────────────────────────────────────────────────────────────────────
236 // --name/--hostname: Fixed names for scripts (`docker exec aesthetic`)
237 //
238 // -v /tmp/.X11-unix: X11 socket mount for clipboard sharing
239 // Works with DISPLAY env var for xclip/xsel
240 //
241 // --cap-add=SYS_PTRACE: Allow ptrace for debugging (strace, gdb attach)
242 // --security-opt=apparmor=unconfined: Disable AppArmor for debug tools
243 //
244 // --init: Use tini as PID 1 to reap zombie processes
245 //
246 // --memory=12g: Cap container memory (prevents OOM killing host)
247 // --shm-size=2g: Shared memory for Chrome/Puppeteer headless rendering
248 //
249 // --tmpfs: In-memory /tmp for faster temp file operations
250 //
251 // --ulimit nofile: Raise open file limit for Vite's many watchers
252 // --add-host: Force Docker host alias inside the container
253 // Needed when Docker Desktop doesn't inject host.docker.internal
254 //
255 // NOTE: Explicit -p port mappings removed - forwardPorts handles this
256 // and avoids "port already in use" errors on restart
257 "runArgs": [
258 "-v", "/tmp/.X11-unix:/tmp/.X11-unix",
259 "-q",
260 "--add-host=host.docker.internal:host-gateway",
261 "--cap-add=SYS_PTRACE",
262 "--security-opt=apparmor=unconfined",
263 "--security-opt", "label:disable",
264 "--name", "aesthetic",
265 "--hostname", "aesthetic",
266 "--init",
267 "--memory=12g",
268 "--memory-swap=12g",
269 "--shm-size=2g",
270 "--ulimit", "nofile=131072:131072"
271 ],
272
273 // ─────────────────────────────────────────────────────────────────────────────
274 // LIFECYCLE COMMANDS
275 // ─────────────────────────────────────────────────────────────────────────────
276 //
277 // initializeCommand: Runs on HOST before container starts
278 // - Removes stale "aesthetic" container (prevents rebuild conflicts)
279 // - Creates required directories (.emacs.d, envs, tezos-data)
280 // - Ensures fish_history exists (prevents volume mount failures)
281 // Note: runs as array to avoid shell interpretation issues
282 "initializeCommand": ["sh", "-c", ".devcontainer/scripts/prepare-host.sh"],
283
284 // postStartCommand: Runs INSIDE container after it starts
285 // - Wrapper script that backgrounds entry.fish and returns quickly
286 // - entry.fish sets up:
287 // • Docker daemon (docker-in-docker)
288 // • Emacs daemon with eat-mode terminals (artery, fishy, etc.)
289 // • Fish shell environment (fnm, vault secrets)
290 // • CDP tunnel for VS Code remote control
291 // • Creates .waiter file when ready (for aesthetic-launch.sh)
292 "postStartCommand": "sh .devcontainer/scripts/poststart-wrapper.sh || (echo '[postStart] wrapper failed, using entry fallback'; if [ -r .devcontainer/entry.fish ]; then nohup fish .devcontainer/entry.fish > /tmp/entry-fallback.log 2>&1 & else nohup fish /entry.fish > /tmp/entry-fallback.log 2>&1 & fi)",
293
294 // postAttachCommand: Runs when VS Code attaches to the container
295 // - Lightweight confirmation message
296 // - VS Code's "💻 Aesthetic" task handles actual emacsclient connection
297 "postAttachCommand": "sh .devcontainer/scripts/post-attach.sh"
298
299 // ═══════════════════════════════════════════════════════════════════════════════
300 // CONFIGURATION COMPLETE
301 // ═══════════════════════════════════════════════════════════════════════════════
302 // All features restored except:
303 // - --cgroupns=host (causes container exit on this Fedora host)
304 // - --tmpfs=/tmp:noexec,nosuid,size=2g (causes container exit)
305 // - Explicit -p port mappings (conflict with VS Code's forwardPorts)
306 // - codespaces-linux-var-lib-docker volume (not needed for local Docker socket)
307 // ═══════════════════════════════════════════════════════════════════════════════
308}