Local App reference

A Local App is a static application stored in an agent's apps/<app-id>/ directory and served at /apps/<agent-id>/<app-id>/. See Local Apps for the model and Host local apps for a working example.

Manifest contract

The root manifest.json is optional. Only when that directory entry is absent, the runtime derives the following metadata in memory:

FieldDefault without a manifest
id, nameApp directory name
version"0.0.0" (undeclared-version placeholder, not a cache fingerprint)
entry"index.html"
descriptionnull

The default entry must be a readable regular file inside the app root. The runtime does not guess another HTML file, recurse for an entry, create directories, or write a manifest. A missing manifest.json asset request still returns 404; discovery exposes the derived metadata, not a virtual downloadable manifest.

If present, manifest.json must be no larger than 64 KiB and retain these required fields:

FieldRequiredContract
idYesMust match the app directory name and requested app ID.
nameYesNonempty display name.
versionYesNonempty version string.
entryYesSafe, nonempty relative path to an HTML file inside the app root.
descriptionNoShort description.

Invalid JSON or fields, an unreadable manifest, a directory, a dangling or escaping symlink, or a manifest exceeding 64 KiB is an error even if index.html exists. A contained symlink must resolve to a readable regular file within the same size bound. No malformed explicit manifest falls back to defaults. Custom entries remain supported; a missing explicit entry file continues to return 404 when accessed.

The server resolves paths from the owning agent's home. Clients send logical agent and app IDs, not filesystem paths. App discovery skips invalid entries; an existing agent without an apps/ directory has an empty app list.

Browser SDK

Load the runtime-provided script with <script src="holon.js"></script> from the app's root entry page. It exposes window.Holon.

context()

Returns a promise containing the SDK version, owning agent, app, and session:

{
  "sdk_version": "1",
  "agent_id": "main",
  "app_id": "ping-card",
  "session": { "authenticated": true }
}

Successful access does not make app input operator instructions.

request(requestType, payload?, requestId?)

Posts a versioned request to the app's request endpoint. The response contains ok, version, request_id, agent_id, app_id, status, and message_id. status: "accepted" means the message entered the queue. It does not report business completion or guarantee that the agent will carry out the request.

Requests retain webhook provenance with source app:<app-id>. A request type such as ping is application data; it is not a registered runtime command.

events(options?)

Returns an async iterable of app events. Options are signal (an AbortSignal) and lastEventId (a sequence string for resuming). Breaking out of iteration or aborting closes the stream.

Each event contains version, agent_id, app_id, and an event object with sequence, type, timestamp, message_id, and an optional request_id. The current stream reports message_enqueued and message_processing_started for requests from this app. It is not a stream of completed results, and it does not expose all agent activity.

Replay uses a bounded recent-event window. After a disconnect, verify work state rather than treating a missing event as evidence that nothing happened.

Hosting and access limits

Daemon configuration

local_apps is a daemon-wide configuration object, not a manifest field:

KeyDefaultValidation
local_apps.max_asset_bytes67108864Positive integer byte count; zero is not unlimited.
local_apps.cspUnset (null)Complete HTTP header value; invalid values are rejected.

Configure a 128 MiB limit and an optional same-origin policy:

holon config set local_apps.max_asset_bytes 134217728
holon config set local_apps.csp "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"

This policy is an example, not a default or a Unity compatibility guarantee. Use holon config unset local_apps.max_asset_bytes or holon config unset local_apps.csp to restore the corresponding default. With a running daemon, config set and config unset use the existing configuration API to persist changes and schedule configuration reload. After a successful reload, subsequent App requests use the new settings; in-flight streams retain the policy already acquired for their request. The CLI does not silently fall back to offline writes when an update is rejected. Direct configuration-file edits do not trigger reload automatically. Offline settings take effect when the daemon starts or restarts. Apps cannot override these settings. Web GUI and Explorer size/CSP/sandbox policies are unchanged.

Transfer semantics and capacity

Entry pages and assets support GET, HEAD, single Range requests (206/416), ETag, 304, and If-Range. HEAD ignores Range, sends no body, and does not read file content. Responses retain Cache-Control: no-store and X-Content-Type-Options: nosniff; validators do not promise persistent caching.

Files stream in 64 KiB chunks rather than being buffered whole, with reads capped at the checked response length. File growth cannot enlarge the response; truncation or I/O errors interrupt the stream. App and Explorer share this transport code, not authorization or publication policy.

Each HTTP AppState shares 16 asset-stream permits across all agents and apps. When all are held, another asset request returns 503 / app_asset_busy with Retry-After: 1. Body completion, errors, or dropping the response release the permit; bodyless responses release it promptly. SDK and API endpoints do not consume these permits. The stream-buffer budget is not a process RSS cap.

Unity and deployment scope

The initial target is uncompressed, single-threaded Unity Web output. .gz, .br, and .unityweb are downloadable as ordinary files, but that does not promise runnable precompressed builds or correct original MIME/Content-Encoding negotiation. Multithreading, COOP/COEP, SharedArrayBuffer enablement, reverse proxying, and server-side execution are not added.

Real-game and real Mac/Safari acceptance have not been completed. Automated HTTP checks are not evidence of those results. Existing deployments need an updated binary and configuration activation; this documentation does not deploy or restart a running service.

The HTTP control plane owns the route and authentication reference. See Execution and access boundaries before exposing a runtime remotely.