Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
8714b2c
Derive every Core address from one public URL and report installation…
SaladDay Sep 25, 2026
1431360
Close E2B admission on a loopback public URL and match Core's origin …
SaladDay Sep 25, 2026
32b661e
Read Core's address from config.json on the Nodes page
SaladDay Sep 25, 2026
9f8e26f
Show how to call Core under a newly issued key
SaladDay Sep 25, 2026
d26ddd2
Show installation facts and startup settings on System
SaladDay Sep 25, 2026
9642ec5
Cover the public address and installation read in browser acceptance
SaladDay Sep 25, 2026
b2023f8
Describe the read-only public address and installation read in Web docs
SaladDay Sep 25, 2026
76948f5
Match the Web to the backend's installation and node address client
SaladDay Sep 25, 2026
11c1645
Name every model provider source on the how-to-call card
SaladDay Sep 25, 2026
39400cd
Name the nodes on an old Core address from the node list
SaladDay Sep 25, 2026
d8b620e
Export the how-to-call variables together and report failed copies
SaladDay Sep 25, 2026
e71c1db
Say why Save waits and when the address is loopback in the wizard
SaladDay Sep 25, 2026
aed8f99
Answer the fixture's address errors as Core does
SaladDay Sep 25, 2026
09acacd
Describe the public address without assuming HTTPS on System
SaladDay Sep 25, 2026
36f340a
Describe the E2B address rule as loopback, not HTTPS
SaladDay Sep 25, 2026
b6aa8de
Hold each System section's place while it loads, announced once
SaladDay Sep 25, 2026
3e9217d
Follow Core's deployment write order and counts in the fixture
SaladDay Sep 25, 2026
72b4743
Polish the stale-node list, copy labels and SDK pin
SaladDay Sep 25, 2026
30cbf38
Match Core's current E2B public URL message in the fixture
SaladDay Sep 25, 2026
a1b623f
Merge origin/main (#137) into the 1a Web branch
SaladDay Sep 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ __pycache__/
/state/
/cache/
/target/
# `go build ./services/core-console` in the repository root writes this binary.
/core-console
/playwright-report/
/test-results/
/.agents/
Expand Down
40 changes: 21 additions & 19 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ intervals and restart gaps. Do not substitute host usage for process usage.

Administrator node detail adds host observations and history as documented in
[node-host-history.md](contracts/agents-api/node-host-history.md). Keep the node
list unchanged. Reuse authenticated heartbeat ownership, the Runtime sampling
list unchanged apart from the address each node enrolled with (`core_url`). Reuse authenticated heartbeat ownership, the Runtime sampling
sweep and PostgreSQL retention cleanup; node observations have their own table
because they do not belong to a Project, Session or Environment. History is
best-effort telemetry, never scheduling truth. No read-triggered sampling or
Expand Down Expand Up @@ -737,8 +737,9 @@ server receives the private key, uses it for sign-in and injects it only on
never receives that key. Node and daemon connections use `/api/v1`
with their own credentials; the reverse proxy sends them directly to Core, never
through Web. Zero-node Core receives neither the Docker socket nor KVM.
The Web and deployment administrator API select one provider, public Core origin,
per-sandbox resources and immutable Runtime release. PostgreSQL owns this complete,
The Web and deployment administrator API select one provider, per-sandbox
resources and immutable Runtime release; the Core address comes from the
installation public URL. PostgreSQL owns this complete,
generation-tagged selection under the existing execution lease and deployment lock.
The shared `sandbox.DeploymentSpec` defines required CPU/memory and supported disk
limits plus Runtime provenance; neither a node file nor the installer owns another
Expand Down Expand Up @@ -779,13 +780,13 @@ A locally unavailable provider dependency keeps hosted admission closed while th
existing scan waits for repair; administrator recovery remains available, including
on restart. Database and ownership errors remain failures. Unconfigured hosted
admission creates no Session state. Derive Runtime bootstrap and daemon WebSocket
addresses from the saved validated origin, never inbound Host headers. Read the
addresses from the validated installation public URL, never inbound Host headers. Read the
current selection from the live deployment API; there is no startup configuration
read.

Provider, resources and Runtime changes require maintenance, the current generation
and verified zero retained or pending resources. The Core origin remains unchanged
by this operation. Maintenance prevents fresh hosted Sessions and allocations while
and verified zero retained or pending resources. They never change the Core
address, which comes from the installation public URL. Maintenance prevents fresh hosted Sessions and allocations while
retaining admitted work, known receipts, queries and explicit cleanup. Unreleased
allocations include stopped compute, snapshots, uncertain operations and pending
cleanup. Pending hosted Environments without allocations also block changes.
Expand Down Expand Up @@ -1652,8 +1653,8 @@ execution concurrency bounds simultaneous work, not attempt frequency. Managed-p
polling retains its separate five-second interval. Unknown promotion results or
errors after admission retain the existing no-replay settlement rules.

`AGENTS_API_DAEMON_WS_URL` enables the private gateway and supplies the unchanged
public `remote_url`. `AGENTS_API_HARNESSES` explicitly adds deployment-supported
`AGENTS_API_PUBLIC_URL` enables the private gateway; Core derives the public
`remote_url` from it. `AGENTS_API_HARNESSES` explicitly adds deployment-supported
engines to the default engine and configured managed profiles; advertising a
heartbeat alone does not enable an engine. The three native profiles share enrollment
at `/workspace`. Their new user-managed public chain requires fixed-client/raw HTTP,
Expand Down Expand Up @@ -1791,14 +1792,15 @@ Environment/key binding and current authenticated connection; it never enrolls,
allocates, wakes a sandbox or grants project resource access. It is an `/api/v1`
machine route that reaches Core directly, never through the console. Bounded
polling and reruns retain the original container and history; timeout is a
diagnostic failure, not permission to relaunch. An explicit installer
`--public-url` supplies both the console origin and the advertised daemon `wss`
origin. Keep local managed Provider routing separate; do not return an internal
Compose hostname to a user-managed Runtime when an external origin was supplied. Bootstrap routing uses the
node bound to the authenticated device's persisted allocation, never request Host
or caller-supplied placement fields. Local and remote managed nodes use the saved
public Core origin. Self-hosted devices retain the deployment's advertised public
address. This does
diagnostic failure, not permission to relaunch. The installation public URL
(`AGENTS_API_PUBLIC_URL`, from the installer's `--public-url`) is the one origin for
applications, nodes, sandbox guests and self-hosted executors, and also the console
origin. Core derives the daemon `wss` URL, the self-hosted `remote_url`, hosted
Runtime bootstrap and the deployment's read-only `core_url` from it; the deployment
API does not accept a Core address, and no deployment row stores one. Bootstrap
never uses request Host or caller-supplied placement fields. Each node records the
address it enrolled with; after the public URL changes, it receives no new
sandboxes until re-added. This does
not widen sandbox network policies or change credential admission.

The distribution build sets umask 022 for non-root-readable payloads; installation
Expand Down Expand Up @@ -1879,7 +1881,7 @@ liveness only, never invented traffic or work; offline/stale connections are
static and reduced-motion preferences disable decorative animation. Node selection
reveals inspection details. Installation identifiers, provider metadata and
allocation records are secondary content. Node enrollment is an explicit Add node action in a focused
dialog, using the saved Core origin or the paired console origin by default.
dialog, using the deployment's `core_url` (the installation public URL).
Do not expose routine network wiring or manual runtime setup as the primary flow.
Generate a one-time command only on user intent, never retry enrollment writes
automatically, and discard credentials and late responses when the dialog closes
Expand Down Expand Up @@ -2404,7 +2406,7 @@ replaced; do not carry obsolete compatibility code forward to satisfy this secti
tenant ownership and a credential digest. Their internal daemon gateway uses
`/api/v1/agent-daemon/*`, separately from the official `/v1/agents/*` surface;
device credentials grant no Session API or product permissions. The optional
`AGENTS_API_DAEMON_WS_URL` enables that gateway. It is a single-process registry,
`AGENTS_API_PUBLIC_URL` enables that gateway. It is a single-process registry,
not a claim of multi-pod execution or stock `exec-server` interoperability.
Self-hosted enrollment uses this gateway with an exact Environment binding.
Session/device bindings are tenant-scoped and immutable. Revocation denies new
Expand Down Expand Up @@ -2775,7 +2777,7 @@ replaced; do not carry obsolete compatibility code forward to satisfy this secti
including after terminal or later Turns. Omitted/null input is permitted only
for non-streaming hosted creation and self-hosted creation.
Creation streaming uses the shared live path above; non-text messages remain a gap.
- Enabling `AGENTS_API_DAEMON_WS_URL` also starts a bounded execution worker. Select
- Enabling daemon transport with `AGENTS_API_PUBLIC_URL` also starts a bounded execution worker. Select
only connected, capable devices owned by the authenticated tenant; bind once and
preserve native continuity. Metadata cannot select a device. Offline work stays
queued and can be cancelled. An engine host is not a self-hosted environment.
Expand Down
35 changes: 32 additions & 3 deletions apps/web/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,17 @@ request runs.
red underneath. The issued key appears in a read-only field with a copy button,
under a notice that it is shown once; only "I've saved this key" dismisses it.
Closing the dialog moves the key into a pending notice card on the page.
- **How to call**: wherever a new key is shown, a card under it gives three
copyable samples, each a Margin Gray block with a Hairline and its label and copy
button in a header row: a Shell block exporting `OPENAI_BASE_URL` (the
installation's API base URL) and `OPENAI_API_KEY` (the new key) together, a curl
request and a Python example with the pinned SDK. A copy the clipboard refuses
selects the sample and says so in red underneath. One Graphite line says running
an Agent needs a model provider: in each request, saved on the Agent, or the
deployment default. When the public address is loopback, a note above the
samples says the API is reachable only on the Core machine; without a public
address only a note to set one shows. Before the installation is read, a
skeleton holds the first sample's place.

### Navigation
Sidebar groups Monitor, Resources and Platform with 12px Graphite group labels;
Expand Down Expand Up @@ -567,7 +578,9 @@ data on screen, projects that could not be read, and other failed actions are
reported in an error toast with the reason. Only when a page or section has nothing to show
does an error state take the place of its content; errors inside a dialog or a form
stay beside what they concern. Coverage notes (Margin Gray, Hairline, 12px corners,
12.5px Graphite) state bounded aggregation. Partial-data chips are amber-tinted pills
12.5px Graphite) state bounded aggregation. A standing warning that needs action,
such as the Nodes page naming nodes still bound to an old Core address, is an
amber-tinted line at the top of the page body. Partial-data chips are amber-tinted pills
with a help tip. Safety notices (a key shown once, a destructive consequence) stay
visible in body text.

Expand Down Expand Up @@ -617,13 +630,29 @@ each sandbox takes the template build's size), then a review.
Choices are large cards that advance on a click; short indigo dashes show the
progress; pages slide and blur across. The review states where sandboxes run,
the size, the Runtime (taken from this console's distribution manifest) and the
Core address, asking for it only when the console's own address cannot serve.
Core address, read-only: it is config.json's `public_url`, and the console never
asks for it. A loopback address carries an amber line under it: only the Core
machine reaches it. When Core rejects the configuration for it (E2B with a
loopback `public_url`), a red-tinted block under the review keeps Core's message
and adds the config file and apply command as copyable values. A save attempt
clears the E2B key, so the review then says to enter it again, with a link to
that step.
Advanced settings, one link away, hold the complete form: resources (not for
E2B), the Core address, the Runtime release and the E2B template. A change keeps the saved size
E2B), the Runtime release and the E2B template. A change keeps the saved size
and Runtime while the backend stays the same (a saved size outside the presets is
offered as Current); another backend starts from its standard size and this
console's Runtime, and E2B always needs its key again. Rules sit behind help tips.

### System page
Read-only facts in three sections, each saying where it changes. Installation: the
public address, API base URL, installation ID and source commit as a fact card.
Sandboxes: the shared sandbox configuration, with a "Change on the Nodes page"
text action in the section header. Startup settings: a line naming the config file
and the apply command as copyable chips, with when they were last applied, over a
table of each setting, its value and the services a change restarts. Sensitive
settings show only Configured or Not set; Default and Fixed after install are
neutral pills beside the value.

### Loading and motion
The console has no spinners and no "Loading…" lines. Reads are cached (TanStack
Query) and prefetched on navigation hover, so revisits show data at once and
Expand Down
10 changes: 6 additions & 4 deletions apps/web/PRODUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,10 @@ workbench.
template build's), a review, and advanced settings
with the complete form — then the node list with each node's capacity, host
figures and allocations, enrollment, renaming, sandbox limits and removal; Add node
asks for the node's sandbox limits before it issues the one-time command), System (only the sandbox
configuration every project shares: where sandboxes run, each sandbox's size, the
Runtime or E2B template build, microsandbox's idle suspension, the Core address and maintenance).
asks for the node's sandbox limits before it issues the one-time command), System (read-only: the
installation's public address, API base URL, installation ID and source commit; Core's
startup settings from config.json, with the file and the apply command that change them;
and the sandbox configuration every project shares, with a link to Nodes where it changes).
- A node whose provider is not ready names the reason (Docker unreachable, no Docker
limits, missing Runtime image, no KVM, missing microsandbox components, a host too
small) and its fix in the help tip beside its status, wherever that status shows.
Expand Down Expand Up @@ -113,7 +114,8 @@ workbench.
its named API keys; projects do not see each other's assets. Issuing or revoking
a key never touches assets. Archiving a project revokes every key and keeps its
assets viewable and deletable. Key plaintext is shown once, at issuance, and never
stored by the console.
stored by the console. Beside it the console tells developers to set
`OPENAI_BASE_URL` (the installation's API base URL) and `OPENAI_API_KEY` (the key).
- **Web API only.** Every read and write goes through `/core/v1/**`. The console
holds no API key and sends nothing to `/v1`.
- **No asset writes except delete.** Assets are created and changed only by
Expand Down
9 changes: 6 additions & 3 deletions apps/web/e2e/console.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@ export const FIXTURE_CORE_KEY = "fixture-core-key-3f9a2c71";

/**
* Fixture state options: `fresh` is a new install (no project, Session or Runtime),
* `sandbox` the sandbox deployment, and `nodes: "none"` a deployment no node has joined.
* `sandbox` the sandbox deployment, `nodes: "none"` a deployment no node has joined, and
* `installation` how config.json's public_url is set: "public" (HTTPS, the default), "local"
* (loopback: only the Core machine reaches the API, and E2B is rejected) or "stale" (public,
* with a node enrolled with an earlier address).
*/
export interface FixtureOptions { fresh?: boolean; sandbox?: "configured" | "none" | "e2b"; nodes?: "none" }
export interface FixtureOptions { fresh?: boolean; sandbox?: "configured" | "none" | "e2b"; nodes?: "none"; installation?: "public" | "local" | "stale" }

/** Fresh fixture state: signed out ("login") or already signed in ("authenticated"). */
export async function resetFixture(request: APIRequestContext, auth: "login" | "authenticated" = "authenticated", options: FixtureOptions = {}) {
await request.post(`${fixture}/__fixture/reset?auth=${auth}${options.fresh ? "&projects=none" : ""}&sandbox=${options.sandbox ?? "configured"}${options.nodes ? `&nodes=${options.nodes}` : ""}`);
await request.post(`${fixture}/__fixture/reset?auth=${auth}${options.fresh ? "&projects=none" : ""}&sandbox=${options.sandbox ?? "configured"}${options.nodes ? `&nodes=${options.nodes}` : ""}&installation=${options.installation ?? "public"}`);
}

/** Opens the console already signed in, in English. */
Expand Down
Loading
Loading