Surface Contract
surface.elonn.local publishes the member-scoped runtime surface contract consumed by world.elonn.local.
World reads:
GET /runtime
World supplies trusted member context in X-Elonn-Member-Id when it reads Surface runtime state and embeds the returned surface_runtime object in /world/session.
Surface is the authority for durable surface state only: surface identity, stack membership, focus, placement, permissions, content binding, rendering hints, and surface session lifecycle. Service content remains owned by the service named in service_binding and loaded through content_source.
Surface also owns resource translation: converting external representations into runtime-safe Surface Objects. This does not transfer ownership of the source resource to Surface.
Runtime payload
{
"contract": {
"name": "elonn.surface.runtime",
"version": 1
},
"surfaces": [],
"stacks": [],
"viewports": []
}
Surface object
Each entry in surfaces is a durable surface record.
| Field | Type | Meaning |
|---|---|---|
numeric_id |
integer | Database id, internal to Surface |
surface_id |
string | Stable runtime id, for example surface_find |
surface_type |
string | Normalized surface type |
title |
string | Runtime display title |
service_binding |
object | Service/resource identity for the surface |
content_source |
object | Where the runtime gets body content |
runtime_state |
object | Runtime-safe state summary |
permissions |
object | Runtime affordance flags |
rendering_hints |
object | Non-authoritative presentation hints |
placement |
object | Current placement mode and metadata |
content_source.kind is usually runtime_panel or inline. A runtime_panel URL points to a World panel endpoint such as /world/panels/social; the source service still owns the panel data. In the current runtime contract, runtime_panel is a content source, not a user-visible carry-panel product surface. Real workspaces render into durable center-stack surfaces, while transient notices may still use a short-lived panel bridge.
Object-valued fields are always JSON objects. Empty service_binding, content_source, runtime_state, permissions, rendering_hints, and placement.metadata values serialize as {}, not [].
Canonical durable workspace types: web_surface, document_surface, map_surface, calendar_surface, conversation_surface, community_surface, event_surface, finding_surface.
Canonical runtime/context types: profile_surface, dashboard_surface, service_nav_panel (transient service navigation or notice panel).
Web-extracted types produced by multi-surface translation:
| Type | Produced when |
|---|---|
web_business_surface |
Schema.org LocalBusiness/Restaurant/Organization/FoodEstablishment found |
web_hours_surface |
openingHours or openingHoursSpecification found on any schema object |
web_menu_surface |
hasMenuSection/menuSection or @type: Menu/MenuSection found |
web_events_surface |
One or more future @type: Event objects found |
web_gallery_surface |
Two or more distinct ImageObject entries found |
web_article_surface |
@type: Article, NewsArticle, BlogPosting, or Review found |
web_contact_surface |
@type: ContactPage found |
Surface Objects and translation
runtime_state.surface_object contains normalized content that any runtime can
render without parsing the source format:
{
"surface_object_id": "surface:...",
"source_finding_id": "brave:...",
"object_type": "resource",
"title": "...",
"summary": "...",
"primary_url": "...",
"branding": {},
"content": {},
"actions": [
{"id": "save", "label": "Save", "type": "save_object"},
{"id": "share", "label": "Share", "type": "share_object"}
],
"runtime_hints": {}
}
primary_url is provenance, not a runtime navigation instruction. Surface
Objects never contain arbitrary HTML, CSS, scripts, forms, tracking markup,
advertisements, cookie prompts, or browser chrome.
Actions are ordered semantic descriptors. Surface owns their labels and types;
runtimes do not infer actions from object_type or translate string names into
UI.
An html_resource finding starts as a lightweight finding surface. A member
action calls the World translation command, and Surface runs the multi-surface
translator against the resource.
Multi-surface path (structured pages): When Schema.org JSON-LD is present,
the translator runs type-specific extractors in priority order (Business →
Article → Hours → Menu → Events → Gallery → Contact). Each extractor that finds
sufficient data returns a typed surface object. Surface creates one new surface
record per extracted object and replaces the original finding surface's slot in
the finding_overlay stack with the new typed surfaces. The original finding
surface is retained in the database (not deleted) with
runtime_state.translation.status = "expanded" so the raw finding payload is
available for audit or re-translation.
Fallback path (unstructured pages): If the multi-surface translator returns
no surfaces (no JSON-LD or no recognized types), Surface falls back to the
html_translator, which reads Schema.org JSON-LD, canonical and standard
metadata, OpenGraph, Twitter card metadata, icons, theme color, publisher,
title, description, and images. It produces a single generic resource or
place object and upgrades the finding surface in place. Repeated translation
on this path returns the persisted object unless an authorized refresh is
requested.
Cache behaviour: A finding surface already in the expanded state (has
runtime_state.translated_surface_ids) returns the current runtime immediately
without re-fetching unless refresh=true is supplied in the translate request.
A place finding opens as a durable map_surface in carry_primary. Surface
converts the finding into a place Surface Object and requests a
provider-neutral map presentation from Maps. The original finding remains a
finding; opening it does not move the finding surface into the center stack.
Repeated opens of the same provider/source id focus the same map surface.
The place Surface Object includes content.location,
runtime_hints.presentation_status, and runtime_hints.map. The map value
uses the elonn.map.runtime contract and contains a viewport, declared layers,
markers, and attribution. A runtime renders those declared assets natively; it
does not receive OSM response formats or embedded browser content.
Stack object
Each entry in stacks is a member-scoped ordered collection of surfaces.
| Field | Type | Meaning |
|---|---|---|
numeric_id |
integer | Database id, internal to Surface |
stack_id |
string | Stable stack id, for example carry_primary |
stack_type |
string | Stack role, for example carry_stack |
region |
string | Runtime region, for example carry or context |
display |
string | Runtime display mode, for example full or rail |
slot |
string | Optional contextual slot |
orientation |
string | Stack navigation orientation |
focused_surface_id |
string | Current focused surface id |
surface_order |
array | Ordered list of surface ids |
viewport_id |
string | Viewport that presents the stack |
Canonical stack ids:
| Stack | Role |
|---|---|
carry_primary |
Center workspace |
left_context |
Runtime and user context rail |
right_context |
Service navigation or field controls rail |
Viewport object
Each entry in viewports describes a runtime presentation region.
| Field | Type | Meaning |
|---|---|---|
viewport_id |
string | Stable viewport id |
viewport_type |
string | Viewport behavior, stack |
region |
string | Runtime region |
slot |
string | Optional contextual slot |
display |
string | Display mode |
neighbor_visibility |
number | Adjacent surface visibility hint |
Runtime writes
Runtimes write through World. World derives trusted member context and relays the write to Surface.
Place findings use:
POST /world/surface/surfaces/{surface_id}/open
The successful response contains the opened surface and the complete updated
surface_runtime. The runtime applies that state, enters carry mode, and
focuses the returned map surface.
POST /world/surface/stacks/{stack_key}/focus
POST /world/surface/stacks/{stack_key}/reorder
POST /world/surface/surfaces/{surface_id}/placement
POST /world/surface/surfaces/{surface_id}/translate
focus body:
{
"surface_id": "surface_social"
}
reorder body:
{
"surface_order": ["surface_find", "surface_social", "surface_calendar"]
}
placement body:
{
"placement_mode": "stacked",
"placement": {
"stack_id": "carry_primary",
"region": "carry",
"display": "full"
}
}
Allowed placement modes: stacked, docked, detached, shared, field_anchored.
Translation has no runtime-supplied URL. Surface reads the persisted finding
from the member-owned surface, validates and fetches its public HTTP(S) source,
and returns the updated surface_runtime.
Surface's internal service endpoints still require trusted member context; runtime clients do not send it.
Member scoping
Trusted member context is part of the Surface runtime contract. Reads and writes for one member must not alter another member's stack rows, focus, order, or placement.
Runtime reads without member context are rejected. The database enforces one stack row per (member_id, stack_key) and does not retain global member_id = NULL stack rows.
Persistence behavior
Runtime reads are additive. When a member runtime asks for surface_runtime, Surface may create missing canonical stacks, append missing default surfaces, and create missing placement rows. It must not clear stack items, reorder an existing stack, overwrite valid focus, or reset an existing placement during a read.
Definition changes are additive:
- a newly introduced default surface is appended to the member's stack
- an existing user order is preserved
- an existing valid
focused_surface_idis preserved - an existing placement is preserved
- a removed or renamed default surface is not automatically deleted from a member stack
focused_surface_id is member-level persisted focus. It is not a realtime cross-runtime synchronization rule. Per-runtime or per-device session focus should be modeled separately from durable member stack membership and order.
Verification
The contract is covered by smoke tests in the producing services:
bash surface.elonn.local/test.sh
bash world.elonn.local/test.sh
Surface verifies trusted member context, normalized payload fields, object-valued JSON shape, focus/order persistence, and cross-member focus isolation.
World verifies that /world/session preserves the normalized surface_runtime object and publishes World relay command URLs without exposing member_id or direct Surface service URLs to runtimes.