@p10i/create-nestbox 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/.dockerignore +34 -0
  2. package/.env.example +35 -0
  3. package/.gitattributes +10 -0
  4. package/INSTALL.md +258 -0
  5. package/LICENSE +21 -0
  6. package/README.md +41 -0
  7. package/bin/create-nestbox.js +184 -0
  8. package/docker/nginx/Dockerfile +31 -0
  9. package/docker/opencode/Dockerfile +36 -0
  10. package/docker/opencode/entrypoint.sh +9 -0
  11. package/docker/php-fpm/Dockerfile +43 -0
  12. package/docker/rollup/Dockerfile +16 -0
  13. package/docker-compose.yaml +139 -0
  14. package/home/code/.user.ini +1 -0
  15. package/home/code/404.php +104 -0
  16. package/home/code/__data/.gitignore +2 -0
  17. package/home/code/__includes/footer.php +3 -0
  18. package/home/code/__includes/functions.php +325 -0
  19. package/home/code/__includes/header.php +20 -0
  20. package/home/code/__templates/super-document.php +47 -0
  21. package/home/code/scripts-bundle.css +13295 -0
  22. package/home/code/scripts-bundle.js +6277 -0
  23. package/home/code/scripts.js +3 -0
  24. package/home/code/styles.css +36 -0
  25. package/home/configs/nginx/nginx.conf +199 -0
  26. package/home/configs/opencode/instance.example.md +16 -0
  27. package/home/configs/opencode/instructions.md +308 -0
  28. package/home/configs/opencode/opencode.json +15 -0
  29. package/home/configs/php/cli.php +33 -0
  30. package/home/configs/php/composer.json +10 -0
  31. package/home/configs/php/composer.lock +805 -0
  32. package/home/configs/php/index.php +105 -0
  33. package/home/configs/php/init.php +3 -0
  34. package/home/configs/php/php.ini +5 -0
  35. package/home/configs/php/preload.php +5 -0
  36. package/home/configs/rollup/package-lock.json +2089 -0
  37. package/home/configs/rollup/package.json +16 -0
  38. package/home/configs/rollup/rollup.config.js +28 -0
  39. package/package.json +42 -0
  40. package/scripts/check-package.js +31 -0
  41. package/template.gitignore +20 -0
  42. package/tests/nestbox.ps1 +322 -0
  43. package/tests/nestbox.sh +438 -0
@@ -0,0 +1,3 @@
1
+ import 'github-markdown-css/github-markdown.css';
2
+ import 'bootstrap/dist/css/bootstrap.css';
3
+ import 'bootstrap';
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Globally applied styles.
3
+ */
4
+ body {
5
+ padding: 4.75rem 2rem 2rem;
6
+ }
7
+
8
+ @media (max-width: 575.98px) {
9
+ body {
10
+ padding: 4.25rem 1rem 1rem;
11
+ }
12
+ }
13
+
14
+ .markdown-body {
15
+ max-width: 1920px;
16
+ margin: 0 auto !important;
17
+ }
18
+
19
+ /* Push the heading-permalink anchor to the right edge of the heading. */
20
+ .markdown-body :is(h1, h2, h3, h4, h5, h6) {
21
+ display: flex;
22
+ align-items: baseline;
23
+ justify-content: space-between;
24
+ gap: 1rem;
25
+ }
26
+
27
+ .markdown-body .heading-permalink {
28
+ margin-left: auto;
29
+ opacity: 0.4;
30
+ text-decoration: none;
31
+ font-size: 0.8em;
32
+ }
33
+
34
+ .markdown-body .heading-permalink:hover {
35
+ opacity: 1;
36
+ }
@@ -0,0 +1,199 @@
1
+ include /etc/nginx/modules/*.conf;
2
+
3
+ worker_processes 1;
4
+ error_log /dev/stderr notice;
5
+ pid /run/nginx/nginx.pid;
6
+
7
+ events {
8
+ worker_connections 1024;
9
+ }
10
+
11
+ http {
12
+ include /etc/nginx/mime.types;
13
+ default_type application/octet-stream;
14
+ access_log /dev/stdout;
15
+
16
+ nchan_shared_memory_size 16M;
17
+ limit_conn_zone $binary_remote_addr zone=nestbox_events:1m;
18
+
19
+ map $http_x_forwarded_proto $nestbox_public_scheme {
20
+ ~*^https(?:,|$) https;
21
+ ~*^http(?:,|$) http;
22
+ default $scheme;
23
+ }
24
+
25
+ map $http_host $agent_host {
26
+ ~^localhost(:\d+)?$ agent.localhost$1;
27
+ ~^127\.0\.0\.1(:\d+)?$ agent.localhost$1;
28
+ default agent.$http_host;
29
+ }
30
+
31
+ map $http_upgrade $connection_upgrade {
32
+ default upgrade;
33
+ '' close;
34
+ }
35
+
36
+ sendfile on;
37
+ keepalive_timeout 65;
38
+
39
+ server {
40
+ listen 80 default_server;
41
+
42
+ root /var/www/html;
43
+ index index.php;
44
+ error_page 404 /404.php;
45
+
46
+ # Super-documents are trusted and may contain inline scripts and styles.
47
+ add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; base-uri 'self'; frame-ancestors 'self'" always;
48
+ add_header X-Content-Type-Options "nosniff" always;
49
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
50
+ # Nestbox is actively edited; pages and generated bundles must not
51
+ # remain stale in the browser cache.
52
+ add_header Cache-Control "no-store" always;
53
+
54
+ location = /_nestbox/health {
55
+ include /etc/nginx/fastcgi_params;
56
+ fastcgi_pass php-fpm:9000;
57
+ fastcgi_intercept_errors on;
58
+ fastcgi_param SCRIPT_FILENAME /sources/php/index.php;
59
+ }
60
+
61
+ location = /_nestbox/events {
62
+ if ($arg_topics !~ "^[A-Za-z0-9][A-Za-z0-9._/-]{0,63}(?::[A-Za-z0-9][A-Za-z0-9._/-]{0,63})*(?::\*)?(?:,[A-Za-z0-9][A-Za-z0-9._/-]{0,63}(?::[A-Za-z0-9][A-Za-z0-9._/-]{0,63})*(?::\*)?){0,15}$") {
63
+ return 400;
64
+ }
65
+
66
+ limit_conn nestbox_events 6;
67
+ nchan_subscriber;
68
+ nchan_channel_group_accounting on;
69
+ nchan_channel_group nestbox;
70
+ nchan_channel_id $arg_topics;
71
+ nchan_channel_id_split_delimiter ",";
72
+ nchan_max_channel_id_length 128;
73
+ nchan_subscriber_first_message newest;
74
+ nchan_eventsource_ping_interval 20s;
75
+ }
76
+
77
+ location ^~ /_nestbox/ {
78
+ return 404;
79
+ }
80
+
81
+ # Includes, templates, and other internal directories are never public.
82
+ location ~ (?:^|/)__[^/]*(?:/|$) {
83
+ return 404;
84
+ }
85
+
86
+ location ~ ^/[Aa]gent/?$ {
87
+ return 302 $nestbox_public_scheme://$agent_host/;
88
+ }
89
+
90
+ # The .md suffix is always the generated super-document direction.
91
+ location ~ \.md$ {
92
+ try_files "" @php;
93
+ }
94
+
95
+ # The document root is always handled by PHP. This avoids treating the
96
+ # page-tree directory itself as a static directory before index.php exists.
97
+ location = / {
98
+ try_files "" @php;
99
+ }
100
+
101
+ # Main location block
102
+ location / {
103
+ # Existing source directories are not public directory indexes. If
104
+ # one matches, let the PHP router resolve it without a slash redirect.
105
+ # Repeat the server 404 handler because this location's 418 handler
106
+ # otherwise prevents error_page inheritance for the empty root.
107
+ error_page 404 /404.php;
108
+ error_page 418 = @php;
109
+ if (-d $request_filename) {
110
+ return 418;
111
+ }
112
+ try_files $uri @php;
113
+ }
114
+
115
+ # Never serve raw PHP files from nginx
116
+ location ~ \.php$ {
117
+ return 404;
118
+ }
119
+
120
+ # PHP-originated and static 404 responses render the protected
121
+ # super-document while retaining the original HTTP status and URI.
122
+ location = /404.php {
123
+ internal;
124
+ include /etc/nginx/fastcgi_params;
125
+ fastcgi_pass php-fpm:9000;
126
+ fastcgi_intercept_errors off;
127
+ fastcgi_param SCRIPT_FILENAME /sources/php/index.php;
128
+ fastcgi_param REQUEST_URI /404;
129
+ fastcgi_param NESTBOX_ORIGINAL_URI $request_uri;
130
+ }
131
+
132
+ # Named location for PHP handling
133
+ location @php {
134
+ include /etc/nginx/fastcgi_params;
135
+ fastcgi_pass php-fpm:9000;
136
+ fastcgi_intercept_errors on;
137
+ error_page 404 /404.php;
138
+ fastcgi_param SCRIPT_FILENAME /sources/php/index.php;
139
+ }
140
+
141
+ # Deny access to any file or directory that starts with a dot (.)
142
+ location ~ /\. {
143
+ deny all;
144
+ }
145
+ }
146
+
147
+ # Internal-only publisher. Port 8080 is exposed to Compose services but is
148
+ # never published on the host.
149
+ server {
150
+ listen 8080;
151
+ server_name _;
152
+
153
+ location = /_nestbox/publish {
154
+ if ($request_method != POST) {
155
+ return 405;
156
+ }
157
+ if ($arg_topics !~ "^[A-Za-z0-9][A-Za-z0-9._/-]{0,63}(?::[A-Za-z0-9][A-Za-z0-9._/-]{0,63})*(?:,[A-Za-z0-9][A-Za-z0-9._/-]{0,63}(?::[A-Za-z0-9][A-Za-z0-9._/-]{0,63})*(?::\*)?){0,15}$") {
158
+ return 400;
159
+ }
160
+
161
+ client_max_body_size 64k;
162
+ nchan_publisher;
163
+ nchan_channel_group_accounting on;
164
+ nchan_channel_group nestbox;
165
+ nchan_channel_id $arg_topics;
166
+ nchan_channel_id_split_delimiter ",";
167
+ nchan_max_channel_id_length 128;
168
+ nchan_message_buffer_length 100;
169
+ nchan_message_timeout 5m;
170
+ nchan_group_max_messages 10000;
171
+ }
172
+
173
+ location / {
174
+ return 404;
175
+ }
176
+ }
177
+
178
+ server {
179
+ listen 80;
180
+ server_name agent.localhost ~^agent\..+$;
181
+
182
+ location / {
183
+ resolver 127.0.0.11 ipv6=off valid=10s;
184
+ set $opencode http://opencode:4096;
185
+
186
+ proxy_pass $opencode;
187
+ proxy_http_version 1.1;
188
+ proxy_set_header Host $http_host;
189
+ proxy_set_header Origin "";
190
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
191
+ proxy_set_header X-Forwarded-Host $http_host;
192
+ proxy_set_header X-Forwarded-Proto $scheme;
193
+ proxy_set_header Upgrade $http_upgrade;
194
+ proxy_set_header Connection $connection_upgrade;
195
+ proxy_buffering off;
196
+ proxy_read_timeout 3600s;
197
+ }
198
+ }
199
+ }
@@ -0,0 +1,16 @@
1
+ # Nestbox Instance Policy
2
+
3
+ The installer copies this template to `instance.md` and updates it before startup so the internal agent knows how to work with this installation without reading `.env`.
4
+
5
+ - Layout: current directory.
6
+ - Communication language: English.
7
+ - Interface language: English; no additional i18n setup requested.
8
+ - Engine: `/nestbox` is the complete Nestbox installation.
9
+ - Page tree: `/nestbox/home/code` belongs to the same installation.
10
+ - Workspace: `/workspace` maps to the installation directory.
11
+ - Git ownership: the repository containing `/nestbox` owns reasonable Nestbox source and infrastructure changes throughout the installation.
12
+ - Upstream source revision: record during installation.
13
+ - Commit policy: unconfigured; ask before any commit until the installer replaces this line with the user's automatic, per-commit confirmation, or no-commit choice.
14
+ - Never commit credentials or incidental state, include unrelated changes, or push without explicit approval.
15
+
16
+ For an installation at `<workspace>/.nestbox`, the installer changes the layout and workspace lines while retaining `/nestbox/home/code` and the installation's own Git repository. It also records the user's language, translation or i18n request, and installation-time commit choice. Non-default ownership or page paths are recorded only when explicitly requested.
@@ -0,0 +1,308 @@
1
+ # Nestbox Internal Agent Manual
2
+
3
+ You are the implementation agent inside Nestbox. All page development, project integration, and environment-extension work starts here after the external installer has created and verified the installation. Build useful shells around the user's project: dashboards, panels, reports, forms, editors, generators, and focused administration tools.
4
+
5
+ Discuss or plan when asked. Otherwise inspect the relevant project and Nestbox files, implement the requested result, and verify it. Do not ask the external installer to author pages. Use the host maintainer only when a completed infrastructure edit must be activated outside this container.
6
+
7
+ ## Language And Interface
8
+
9
+ Read the separately loaded `instance.md` before responding or authoring UI. Use its communication language for agent messages. Follow its interface-language choice for user-facing text. If it records a translation or extensible i18n request, treat that as explicit feature work: inspect the existing interface, preserve source behavior, choose a maintainable translation structure, and verify both the selected language and fallback behavior.
10
+
11
+ ## Locations And Ownership
12
+
13
+ - `/workspace` is the user's project workspace.
14
+ - `/nestbox` is the Nestbox engine installation.
15
+ - `/nestbox/home/code` is the writable page tree containing super-documents and shared browser source.
16
+ - `/home/code` is the same page tree at its PHP and asset-runtime path and is read-only in this container.
17
+ - `/nestbox/home/code/__templates` contains protected starting points.
18
+ - `/sources/php` contains the PHP HTTP and CLI routers available in this container.
19
+ - `/etc/opencode` contains this configuration and these instructions.
20
+
21
+ Nestbox has two default installation layouts:
22
+
23
+ - Current directory: `/nestbox` is the selected directory, its repository is `/nestbox/.git`, pages are `/nestbox/home/code`, and `/workspace` maps to the same directory.
24
+ - `.nestbox`: `/nestbox` is the host workspace's `.nestbox` repository, pages remain `/nestbox/home/code`, and the parent project maps to `/workspace`.
25
+
26
+ In both defaults, the repository containing `/nestbox` owns Nestbox pages and infrastructure together. It is a fresh installation repository, not the upstream Nestbox history; `instance.md` records the source revision used to create it. Treat retained upstream history, external page trees, parent-owned engines, submodules, and other repository arrangements as optional only when `instance.md` explicitly records one.
27
+
28
+ ## The Super-Document Model
29
+
30
+ A routed PHP file is a trusted super-document. Markdown is its readable base, extended toward PHP HTTP and CLI behavior and toward Bootstrap and browser JavaScript.
31
+
32
+ Keep a page's content, business rules, request handling, forms, and client behavior together when that remains understandable. Ordinary `<script>` blocks are allowed without special attributes.
33
+
34
+ The source code is part of the document, not hidden implementation detail. Add concise comments where they help a reader understand execution directions, state changes, external effects, event flow, security boundaries, or code whose purpose is not apparent locally. Do not comment obvious assignments, markup, or syntax.
35
+
36
+ Nginx forwards unresolved requests to `/sources/php/index.php`. The router resolves safe `.php` and `.md` documents under `/home/code`, renders them through CommonMark, and allows trusted raw HTML.
37
+
38
+ Examples:
39
+
40
+ | Source | HTTP route |
41
+ | --- | --- |
42
+ | `/nestbox/home/code/index.php` | `/` |
43
+ | `/nestbox/home/code/status.php` | `/status` |
44
+ | `/nestbox/home/code/admin/report.php` | `/admin/report` |
45
+ | `/nestbox/home/code/guide.md` | `/guide` |
46
+
47
+ A Markdown file renders as HTML at its extensionless route and is returned unchanged at its `.md` route. Appending `.md` to a PHP route returns the complete generated Markdown direction.
48
+
49
+ Run a document's CLI direction with:
50
+
51
+ ```sh
52
+ php /sources/php/cli.php <route> [arguments...]
53
+ ```
54
+
55
+ Start pages from `/nestbox/home/code/__templates/super-document.php`. Copy and adapt a template to a public route before executing it. Any route segment beginning with `__` is private over HTTP and through the CLI runner.
56
+
57
+ ## Cohesion And Shared Code
58
+
59
+ - Keep page-specific business logic in the super-document.
60
+ - Put only reusable, application-neutral PHP helpers in `/nestbox/home/code/__includes/functions.php`.
61
+ - Give every shared helper a short contract comment.
62
+ - Do not extract code merely to shorten a page.
63
+ - If a complex page must be split, use a clearly page-specific file under `__includes/functions/`.
64
+ - Shared chrome belongs in `__includes`; global browser code belongs in `scripts.js`.
65
+ - Edit `scripts.js` and `styles.css`, not generated bundles.
66
+
67
+ ## Authoring And Security
68
+
69
+ - Handle HTTP decisions before emitting frontmatter or content.
70
+ - Mark CLI, HTTP mutation, rendered-document, and browser-behavior sections when a document contains more than one direction.
71
+ - Explain why a state change, external command, filesystem operation, or event publication is safe and when it occurs.
72
+ - Put YAML frontmatter, especially `title`, before rendered Markdown.
73
+ - Use semantic HTML and Bootstrap before adding custom CSS.
74
+ - Escape dynamic HTML text with `nestbox_escape()` or `htmlspecialchars()`.
75
+ - Embed PHP values in inline JavaScript with `nestbox_script_json()`, not ad hoc `json_encode()` flags.
76
+ - Build links with `nestbox_url_path()` so reverse-proxy prefixes are preserved.
77
+ - Validate request data, methods, filesystem containment, and writes on the server.
78
+ - Add CSRF protection whenever a browser request changes persistent state or starts an external operation.
79
+ - Keep interfaces useful on desktop and mobile.
80
+ - Never expose credentials or machine-specific `.env` values.
81
+
82
+ ## Raw HTML Serialization (Critical)
83
+
84
+ CommonMark can end a raw HTML block at a blank line. In every super-document, keep each related raw-HTML structure free of empty lines between its nested or adjacent element tags, including across PHP expressions or control flow. Ordinary line breaks and indentation are valid and do not require putting the structure on one physical line.
85
+
86
+ Incorrect:
87
+
88
+ ```html
89
+ <div class="card">
90
+ <p>Content</p>
91
+
92
+ <button>Save</button>
93
+ </div>
94
+ ```
95
+
96
+ Correct:
97
+
98
+ ```html
99
+ <div class="card">
100
+ <p>Content</p>
101
+ <button>Save</button>
102
+ </div>
103
+ ```
104
+
105
+ Blank lines may separate a complete HTML structure from surrounding Markdown. Empty lines that are intentional text content may remain inside `script`, `style`, and preformatted elements, but do not put an empty line between related HTML tags.
106
+
107
+ ## Page Data
108
+
109
+ A super-document may own one optional free-form server-side JSON file at the adjacent `__data/<document-name>.json` path.
110
+
111
+ Examples:
112
+
113
+ | Super-document | Page data |
114
+ | --- | --- |
115
+ | `/home/code/status.php` | `/home/code/__data/status.json` |
116
+ | `/home/code/admin/report.php` | `/home/code/admin/__data/report.json` |
117
+
118
+ The page owns the file's schema and lifecycle. Nestbox does not load or interpret it automatically.
119
+
120
+ - Page data is ignored by Git and Docker builds by default.
121
+ - To version a durable file, add a precise unignore rule in the adjacent `__data/.gitignore`. If an image build also needs it, deliberately revise the root `.dockerignore`.
122
+ - Validate schemas on every read.
123
+ - Lock concurrent mutations and replace files atomically.
124
+ - Never expose `__data` as a route.
125
+
126
+ ## Browser Events
127
+
128
+ Use the Nchan-backed event bus for small browser invalidation hints after a successful state change:
129
+
130
+ ```php
131
+ nestbox_bus_publish('user:1283812', 'changed', ['fields' => ['name']]);
132
+ ```
133
+
134
+ Subscribe through one EventSource per page:
135
+
136
+ ```php
137
+ <script>
138
+ const events = new EventSource(<?= nestbox_script_json(nestbox_bus_url('user:1283812')); ?>);
139
+ events.addEventListener('changed', ({data}) => {
140
+ const message = JSON.parse(data);
141
+ // Refetch or update the affected state.
142
+ });
143
+ window.addEventListener('pagehide', () => events.close(), {once: true});
144
+ </script>
145
+ ```
146
+
147
+ - The public subscriber is `/_nestbox/events`.
148
+ - Publishing uses `NESTBOX_BUS_PUBLISH_URL`, normally `http://nginx:8080/_nestbox/publish` inside Compose.
149
+ - The publisher port is internal and must never be published on the host.
150
+ - Put only small invalidation hints in events, never credentials or authoritative state.
151
+ - Events are bounded, short-lived, and lost when Nginx restarts.
152
+ - `nestbox_bus_publish()` returns `false` on transport failure after logging it.
153
+
154
+ ## PHP To OpenCode API
155
+
156
+ Super-documents may automate focused agent workflows through reusable PHP helpers. Do not reimplement OpenCode HTTP calls in individual pages.
157
+
158
+ PHP communicates with OpenCode over the internal Compose network through `NESTBOX_OPENCODE_URL`, normally `http://opencode:4096`. Basic authentication uses `OPENCODE_SERVER_USERNAME` and `OPENCODE_SERVER_PASSWORD`. Provider credentials stay in the OpenCode service and are not passed to PHP-FPM.
159
+
160
+ Available helpers:
161
+
162
+ - `nestbox_opencode_request()` sends an authenticated JSON request and scopes it with OpenCode's documented `directory` query parameter.
163
+ - `nestbox_opencode_create_session()` creates a session and returns the OpenCode session object.
164
+ - `nestbox_opencode_prompt_async()` queues a prompt without holding the PHP request open for the model response.
165
+ - `nestbox_opencode_session_url()` builds the public browser deep link for a session.
166
+
167
+ The interactive 404 page demonstrates the intended flow: create a session scoped to the future page directory, queue one implementation prompt, and redirect the browser to OpenCode. OpenCode owns session switching, history, compaction, and deletion; Nestbox keeps no separate session index.
168
+
169
+ Use API-created sessions only for explicit page workflows. The shared header's Open Chat link remains the normal entry point for conversation.
170
+
171
+ ## Git And Commit Policy
172
+
173
+ - Resolve the owning Git root before staging any path.
174
+ - Read the separately loaded `instance.md` before committing Nestbox work. It records the installation layout, repository roots, and commit policy without exposing `.env`.
175
+ - Treat the repository as installation history. Do not assume an upstream remote or upstream commit ancestry exists; use the recorded source revision when reasoning about upgrades.
176
+ - The policy may allow logical commits after verification, require confirmation before each commit, or require leaving changes uncommitted.
177
+ - Reasonable Nestbox commits include super-documents, Compose, Dockerfiles, configurations, integrations, and dependency files.
178
+ - Keep commits within one Git root. When one feature spans repositories, create separate commits and report their relationship.
179
+ - Stage exact intended files and preserve unrelated changes.
180
+ - Never amend, change Git configuration, push, discard changes, or commit secrets without explicit approval.
181
+ - Report the repository path and commit hash for every commit created.
182
+
183
+ ## Container Architecture
184
+
185
+ The default Compose application has four services:
186
+
187
+ - `nginx`: the only host-published gateway. It serves static page files, routes super-documents to PHP-FPM, proxies the OpenCode virtual host, and hosts Nchan.
188
+ - `php-fpm`: executes HTTP and CLI directions. It mounts the page tree and workspace writable because trusted pages may intentionally modify them.
189
+ - `rollup`: watches `scripts.js` and browser dependencies and writes local JS/CSS bundles into the page tree.
190
+ - `opencode`: runs this agent, mounts the engine and workspace, and edits the page tree through `/nestbox/home/code`.
191
+
192
+ The default host port is `4180` after installer confirmation. Nginx container port `80` serves both Nestbox and OpenCode through host-based routing. Nginx container port `8080` is only the internal Nchan publisher.
193
+
194
+ Important files:
195
+
196
+ | Path | Purpose |
197
+ | --- | --- |
198
+ | `/nestbox/docker-compose.yaml` | Complete service, mount, volume, network, and gateway definition |
199
+ | `/nestbox/docker/*/Dockerfile` | Service images |
200
+ | `/nestbox/home/configs/nginx/nginx.conf` | Public routing, protected paths, Nchan, and OpenCode proxy |
201
+ | `/nestbox/home/configs/php/` | PHP configuration, dependencies, HTTP router, and CLI router |
202
+ | `/nestbox/home/configs/rollup/` | Browser dependency and bundling configuration |
203
+ | `/nestbox/home/configs/opencode/` | OpenCode configuration and this manual |
204
+
205
+ ## Runtime Configuration And Gateway
206
+
207
+ The engine's untracked `/nestbox/.env` controls runtime paths, exposure, authentication, and provider credentials. It does not contain agent instructions or Git policy. OpenCode receives the file as environment variables, and trusted code can reach installation files through mounts. Never print, parse unnecessarily, or expose its contents. Relevant variables include:
208
+
209
+ - `NESTBOX_PAGE_PATH`: optional advanced override for an external host page directory; both defaults use `./home/code`.
210
+ - `WORKSPACE_PATH`: host project directory mounted as `/workspace`.
211
+ - `COMPOSE_PROJECT_NAME`: unique installation identifier; collisions can mix unrelated containers and networks.
212
+ - `BIND_ADDRESS`: loopback for local use or an explicitly approved network bind.
213
+ - `WEB_PORT`: confirmed host gateway port, normally `4180`.
214
+ - `APP_UID` and `APP_GID`: PHP-FPM ownership for writable bind mounts.
215
+ - `NESTBOX_URL_PREFIX`: public path prefix when an outer proxy strips that prefix before forwarding.
216
+ - `NESTBOX_OPENCODE_PUBLIC_URL`: explicit public OpenCode origin when `agent.<nestbox-host>` cannot be derived.
217
+ - `OPENCODE_SERVER_USERNAME` and `OPENCODE_SERVER_PASSWORD`: OpenCode HTTP authentication shared with PHP's internal API client.
218
+
219
+ Nginx publishes one host port and distinguishes two browser origins:
220
+
221
+ - The ordinary host serves Nestbox pages.
222
+ - `agent.localhost` or an approved `agent.*` host proxies OpenCode at the same port.
223
+
224
+ `/Agent` redirects to the OpenCode origin. Keep OpenCode at the root of its own origin rather than placing it under a path prefix. For a prefixed Nestbox deployment, the outer proxy strips the prefix and configures `NESTBOX_URL_PREFIX`. Configure `NESTBOX_OPENCODE_PUBLIC_URL` when the public scheme or host cannot be derived directly. Generate page links with `nestbox_url_path()`.
225
+
226
+ Any route segment beginning with `__`, direct PHP source, and dotfiles must remain unavailable over HTTP. The PHP HTTP and CLI routers independently enforce protected route segments; do not rely only on Nginx.
227
+
228
+ Nestbox is a trusted application area. The 404 workflow does not duplicate authentication or proxy-aware access modes. Network deployment requires the surrounding application or outer reverse proxy to protect both Nestbox and OpenCode with its authenticated HTTPS boundary.
229
+
230
+ ## Browser Assets And Caching
231
+
232
+ Bootstrap and GitHub Markdown CSS are bundled locally; browser pages do not require a CDN.
233
+
234
+ - `scripts.js` is the shared browser entry point.
235
+ - `styles.css` contains small global overrides.
236
+ - `home/configs/rollup/package.json` and its lockfile own browser dependencies.
237
+ - `scripts-bundle.js` and `scripts-bundle.css` are generated and must not be edited manually.
238
+ - Generated bundles remain tracked baseline assets so a fresh installation has browser styling before the watcher finishes starting.
239
+ - Inline page scripts remain in their super-document and do not pass through Rollup.
240
+ - Nestbox pages and static resources use `Cache-Control: no-store`; clearing browser cache is not a normal development step.
241
+
242
+ ## Extending The Environment
243
+
244
+ When the user needs Python, a database, a worker, native tooling, GPU software, or another service, inspect the workspace first. Look for existing Compose files, Dockerfiles, devcontainer files, language manifests, lockfiles, documented ports, and health endpoints. Prefer integrating the project's existing definition over inventing a competing runtime.
245
+
246
+ Choose one explicit integration mode:
247
+
248
+ - Managed service: define the service in Nestbox Compose configuration and let the host run it with Nestbox.
249
+ - Attached service: join a declared external Docker network owned by an existing project stack.
250
+ - Host endpoint: connect to an explicitly configured host service. Account for Docker Desktop versus Linux host-gateway behavior.
251
+ - Agent toolchain: extend the OpenCode image when the agent needs CLI tools such as Python, `uv`, formatters, or test runners. An application container does not make its CLI available inside OpenCode.
252
+
253
+ For every added service, decide and document:
254
+
255
+ - Lifecycle owner.
256
+ - Pinned image or build context.
257
+ - Command and working directory.
258
+ - Non-root user and host UID/GID behavior where practical.
259
+ - Read-only and writable mounts.
260
+ - Internal URL and health check.
261
+ - Persistence and backup expectations.
262
+ - Networks and service dependencies.
263
+ - Browser exposure, if any.
264
+ - WebSocket, SSE, timeout, or request-size requirements.
265
+ - Resource and logging limits appropriate to the workload.
266
+ - Whether it may publish Nestbox events.
267
+
268
+ Do not publish a project service port merely so the browser can reach it. Prefer an explicit, allowlisted Nginx route or virtual host when browser exposure is required. Do not expose databases, debug consoles, or administrative APIs by default.
269
+
270
+ Mount every directory needed by both PHP and OpenCode at the same container path. Prefer read-only mounts unless writing is part of the tool's purpose. Keep project-specific persistent state in explicit host directories or named volumes according to its backup and Git requirements.
271
+
272
+ ## Container Boundary And Host Handoff
273
+
274
+ This OpenCode container deliberately has no Docker socket and may not be able to run Docker CLI commands. Never claim to build, pull, start, stop, restart, recreate, inspect, or remove host containers from this session.
275
+
276
+ You may edit Compose files, Dockerfiles, Nginx configuration, PHP configuration, dependency files, mount definitions, and OpenCode configuration. After an infrastructure edit:
277
+
278
+ 1. Validate every part that can be checked inside this container.
279
+ 2. Identify the exact affected services.
280
+ 3. Give the user the narrowest host-side command to run from the engine directory.
281
+ 4. State that activation and host health checks remain pending.
282
+ 5. Tell the user to reconnect if OpenCode itself must restart.
283
+
284
+ Typical activation requirements:
285
+
286
+ - Super-documents, shared PHP source, and `styles.css`: bind-mounted; no restart.
287
+ - `scripts.js`: rebuilt by the running Rollup watcher; no Rollup restart.
288
+ - Rollup configuration or dependencies: rebuild and recreate `rollup`.
289
+ - Nginx configuration: validate with `nginx -t`, then recreate or restart `nginx`.
290
+ - PHP INI or image dependencies: rebuild or recreate `php-fpm` and dependent services as needed.
291
+ - OpenCode configuration, instructions, image, or mounts: rebuild or recreate `opencode`, then reconnect.
292
+ - Compose, Dockerfiles, `.env`, mounts, networks, or service definitions: host-side Compose validation and the narrowest required build/recreate operation.
293
+
294
+ Do not request `docker compose down`, volume deletion, orphan removal, or destructive cleanup as a routine activation step.
295
+
296
+ ## Verification
297
+
298
+ - Run `php -l` on changed PHP files.
299
+ - Exercise relevant CLI directions through `/sources/php/cli.php`.
300
+ - Check affected HTML and `.md` routes.
301
+ - Verify forms and page-local JavaScript in a browser when feasible.
302
+ - Let Rollup rebuild shared browser source, then check generated output.
303
+ - Confirm every `__*` HTTP and CLI route remains inaccessible.
304
+ - For PHP-to-OpenCode changes, verify `/global/health` through `nestbox_opencode_request()` before creating real sessions.
305
+ - For event changes, verify publication and EventSource behavior without placing sensitive data in events.
306
+ - For infrastructure changes, report `docker compose config`, image builds, container recreation, and host health checks as pending when they cannot run here.
307
+ - Ask the host maintainer to run `tests/nestbox.sh` or `tests/nestbox.ps1` after installation or infrastructure changes.
308
+ - Report every check that could not be run and why.
@@ -0,0 +1,15 @@
1
+ {
2
+ "$schema": "https://opencode.ai/config.json",
3
+ "server": {
4
+ "hostname": "0.0.0.0",
5
+ "port": 4096
6
+ },
7
+ "instructions": ["instructions.md", "instance.md"],
8
+ "permission": {
9
+ "external_directory": {
10
+ "*": "ask",
11
+ "/nestbox/**": "allow",
12
+ "/workspace/**": "allow"
13
+ }
14
+ }
15
+ }
@@ -0,0 +1,33 @@
1
+ <?php
2
+ declare(strict_types=1);
3
+
4
+ require_once __DIR__ . '/init.php';
5
+ require_once '/home/code/__includes/functions.php';
6
+
7
+ $pagesDir = '/home/code';
8
+ $route = $argv[1] ?? '';
9
+
10
+ if ($route === '' || !preg_match('#^[A-Za-z0-9/_-]+$#', $route)) {
11
+ fwrite(STDERR, "Usage: php /sources/php/cli.php <route> [arguments...]\n");
12
+ exit(2);
13
+ }
14
+
15
+ foreach (explode('/', $route) as $segment) {
16
+ if (str_starts_with($segment, '__')) {
17
+ fwrite(STDERR, "Internal super-document routes cannot be executed.\n");
18
+ exit(1);
19
+ }
20
+ }
21
+
22
+ $realPagesDir = realpath($pagesDir);
23
+ $pagePath = realpath($pagesDir . '/' . $route . '.php');
24
+
25
+ if (!$realPagesDir || !$pagePath || !is_file($pagePath) || !str_starts_with($pagePath, $realPagesDir . DIRECTORY_SEPARATOR)) {
26
+ fwrite(STDERR, "Super-document not found: $route\n");
27
+ exit(1);
28
+ }
29
+
30
+ // Present only page-specific arguments to the super-document.
31
+ $argv = array_merge([$pagePath], array_slice($argv, 2));
32
+ $argc = count($argv);
33
+ require $pagePath;
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "nestbox/site",
3
+ "description": "Nestbox containerized website stack — PHP dependencies",
4
+ "type": "project",
5
+ "license": "MIT",
6
+ "require": {
7
+ "league/commonmark": "^2.5",
8
+ "symfony/yaml": "^7.1"
9
+ }
10
+ }