Skip to main content

8. Connect the browser interface

A capability contract lets tools and other applications call Studio. A surface lets an operator open its interface from Skulk™. Studio provides both, using the same application logic.

Declare and report a surface​

The manifest declares a Studio surface of kind link and an optional ComfyUI link. Child health reports a SurfaceReport containing the declared surface id, readiness and URL. Skulk™ can then present the application as a discoverable satellite.

Three states remain distinct:

  • The child responds to managed health checks.
  • The Studio HTTP interface is reachable at its reported URL.
  • Ready video model capacity exists for rendering.

The interface can be healthy and useful for managing assets while no model is mounted. It should show the readiness explanation rather than disappear.

The optional ComfyUI URL is an operator-supplied link. It does not automatically connect Studio to another engine or enable arbitrary ComfyUI workflows.

Choose an address the browser can reach​

page_reach controls advertised reach: fabric, loopback or an explicit hostname/address. On hosts that provide a serving address, Studio uses that selection and caller-admission helpers. Its older-host fallback may bind broadly while discovering an advertised address; do not assume all configurations bind only loopback.

A linked surface is a browser URL. Discovery does not create a public tunnel or transparently proxy every application. A loopback URL only works from that machine. Remote access must use a deployment where the operator's browser can reach the advertised interface.

Keep the scoped URL intact​

Studio serves its interface under an opaque, process-generated ticket path, /s/<ticket>/. Routes are relative to that root. Its TypeScript client computes the base from the current page URL and resolves relative API paths against it.

Teaching example: preserve the ticket root
const base = new URL(".", window.location.href);
const planUrl = new URL("api/plan", base);

Using new URL("/api/plan", base) would discard the ticket prefix. Copying the page's absolute URL into a public log would disclose an access-bearing route. These are application concerns, separate from the host's managed-child authentication.

The server checks the ticket and caller admission; state-changing requests also validate origin/fetch-site metadata. Studio does not claim to provide per-user accounts or a general identity platform. Build those controls if your application's deployment requires them.

Reuse handlers, keep transports separate​

server.py runs a threaded HTTP server. It forwards work onto the child's asyncio loop with run_coroutine_threadsafe, rather than creating a second application state machine per HTTP thread. Studio.local routes these requests to the same planning, operation and library services used by managed calls.

Relative routeMethodHandler / purpose
api/readinessGET or POSTExplain live API/model/engine readiness.
api/planPOSTResolve a RenderRequest into a full plan.
api/renderPOSTRender operation verbs.
api/refinePOSTRefinement operation verbs.
api/assetsPOSTLibrary list/import/label/remove operations.
api/takesPOSTKeep/forget/delete take marks.
api/assets/uploadPUTBounded raw file upload.
media/renders/<filename>GETValidated application-owned clip or thumbnail name.
media/assets/<sha256>GETDigest-addressed reference media.
api/refinement/<operation-id>GETComplete saved rewritten prompt.

These are Studio routes under the ticket root, not additional /v1 endpoints in Skulk™. Normative Skulk™ HTTP contracts remain in Foxlight Docs.

Share schemas with the browser​

Pydantic contracts generate schemas/; web/tools/generate-api-types.ts produces TypeScript types from them. The API client owns requests and safe errors. Components receive state through stores instead of performing arbitrary fetches.

Types catch mistakes during compilation; generated TypeScript does not automatically validate every response at runtime. Studio also validates responses at its Python service boundaries. Keep this distinction clear when implementing your own client.

Poll without losing track of work​

Studio polls render operations every three seconds, refinement every two seconds and readiness every 30 seconds. Stores use generation counters to discard stale refresh replies. They hydrate individual operation statuses after bounded list requests and preserve partial batch visibility.

Closing the page stops UI timers. It does not cancel an application-owned render. A user must explicitly cancel work if that is their intent.

For seed sweeps, each member gets its own operation id and plan. Consecutive seeds are varied while the common plan shape must remain consistent. A batch group labels the takes but is not sent as an inference setting. Reusing a seed does not promise bit-identical output across engine builds and hardware.

Beginner checkpoint​

Find one UI action in web/, follow it through the API client into server.py, then locate the Studio method and SDK service it calls. Repeat for Plan and Cancel. You should find shared application behavior with different entry transports.

Next: run a local SDK exercise.