An experimental, prototype Atmospheric (atproto) virtual (2.5d) community workspace for @spacecadets.love fieldwork.spacecadets.love
19 kB · 122 lines
Markdown
at main

Fieldwork: what we are building #

Rewritten 2026-10-14 from Lyre's direction. This is the document to read for what and why; HANDOFF.md says where things stand, GLOSSARY.md fixes the words, and DECISIONS.md dates each choice. Anything marked proposed is an agent's suggestion that Lyre has not approved. Status tags: built, partly, not built.

1. What it is #

Fieldwork is a shared place on atproto. People meet in it (voice, chat, presence), build it from inside it, and use objects in it. Everything you can place from your inventory into the space, and take back, is an object. It is not one app: it is closer to a self-hosted website where the content starts with a venue and an avatar and grows into an environment of socially interactive tiles that people author from within.

  • It is Space Cadets' place and the reference deployment of the platform underneath. It is an experimental prototype for internal use.
  • The underlying platform is called the presence engine: local-first collaborative infrastructure (presence, shared state, voice, records) provided as hooks, so nobody has to rebuild it. Groundwork is the matching governance engine.
  • The part people use is the shell: navigation, bars, panels. The shell is the most important product surface and is deliberately not built from tiles yet, so it can change quickly.
  • The model has an OS feel (compare aetheros.computer): social first, composable, authored from inside itself through its own primitives.
  • "atproto wiki" has been suggested as a better public description than "CMS". Undecided.

2. What is being proven #

Tiles are the center of the project. Today's job is to show that tiles can give the same security and experience guarantees as Cloudflare OS, in a distributed, open social web. Cloudflare OS is the benchmark, not the proof.

A tile has to show all six. None has been audited against the code yet.

  1. Credentials never reach tile code.
  2. Access is granted per resource, not broadly.
  3. Everything a tile reads is logged.
  4. Actions with outside effects wait for a person's approval; reads show them as if applied.
  5. When a room is shared, everyone present is checked against what the tile has already seen.
  6. Tile code has no ambient network access.

The smallest proof: one placeable object with a room face, a panel, Shared State, and one capability from an outside system, with all of it visible in an inspector. The object is an item you pull up and place, not a space of its own. The proof object comes before multiple pages (decided Oct 14).

Which object is still open, and Lyre has reframed it (Oct 14). The first suggestion was a "build board" bound to one GitHub repository. Lyre's direction is wider: a personal dashboard where a person's inventory fills with their own content. They get a picker, paste links to their GitHub work (and other things) into the environment, and each link renders according to the kind of page it is, with actions tied to the data. The aim is to let people express themselves with their own work and content. GitHub is the first outside gatekeeper (section 5); the build board can be the first view the dashboard knows how to render.

A second, later proof uses Google Calendar with the atproto community events lexicon: an event created with an atmo.rsvp-style tool is viewed in the room; the viewer connects their Google Calendar to see what else they have that day and time; RSVPing writes the RSVP record and adds it to their calendar at the same moment. The record types exist: community.lexicon.calendar.event (name, description, startsAt, endsAt, mode, status, locations, uris) and community.lexicon.calendar.rsvp (going, interested, not going), plus query methods getEvent, getRSVP and searchEvents. This needs the Google gatekeeper working first.

Things to look at before building the dashboard (not yet evaluated; check licences and sizes first): a small code or text editor to embed (CodeMirror 6 is the modular, lighter option compared with Monaco; a plain text area is lighter still), and a small slide or presentation renderer (reveal.js is the well-known one). Whether a tile should render content (read-only views) or edit it is a design question that decides how much of this is needed.

3. The application model #

The tile and capability work is written up as its own sub-project in ../capnwebtiles/ (goals, patterns, design choices, challenges). This section keeps Fieldwork's view of it.

An object has some visual presence and is otherwise a web-component configuration holding things such as images, Web Tiles and embeds; the list of what it can hold can grow. A tile is one such kind of content: a Web Tile with several surfaces and some hooks. A tile reaches the space and the shell only through capnweb and XRPC.

  • Surfaces are the ways to use a tile: the face it shows in the room, a panel, a back face. Partly: site/lib/surfaces.js names where apps appear (launcher, bar, sheet, toast, status); how that maps to a tile's surfaces is not designed yet.
  • Hooks are a tile's connections into the space (presence, shared state, voice, records) and the shell. Partly: tile-powers.js lists the powers a tile may ask for; state-capability.js is Shared State v1.
  • Tiles never get write authority. A tile proposes; a person approves in Fieldwork's own UI (propose.js). Built.
  • Room controls are objects. Anything that affects other people's experience and need not live in a person's own shell becomes an object with capabilities that can be shared or delegated. This keeps environmental hooks separate from UI and from authority. Not built.
  • Any lexicon record can get a tile made for it through the craft interface, and tiles compose. Not built.
  • Tiles load through the public loader at load.webtil.es until the spec matures. Built.
  • Parity with the 3D entry now means the same shell features and object interactions. Syncing a common experience with the archived 3D entry is retired. The retirement holds until further notice: the 3D entry (and other engines) are meant to come back later, so the seam of integration built for them stays clean (section 9).

4. Capabilities, keys and inventory #

Capabilities are passed in, never looked up. The old "registry" idea becomes an inspector: a view of the capabilities passed around and what they link. Making capabilities legible, manageable and usable is the lab's research contribution, and there is no known prior art for a live ledger of this kind to copy.

  • Ledger (proposed fields): holder, interface, provider, how it was narrowed, who granted it, when, expiry, status, plus seen (what the tile has read) and pending (actions awaiting approval). Written only by the code that mints capabilities. Not built. Today's one comparable fact is describe().authority, where only the gatekeeper may write delegated.
  • Inspector (proposed): who holds what, a graph, plain words, a diff before installing, a live tap, revoke. Partly: power-ui.js has "Roles & powers", which explains why a tile has a power and what it has done.
  • Outside-system capabilities must be the most legible of all, because they stand for outside systems with sensitive data: trust, but verify.
  • Keys. A door or portal that is not open is locked and needs a key; a key is a capability to enter. Keys can carry delegation settings and appear as literal items in a person's inventory, doubling as bookmarks to pages shared with them. Any object may have a key that decides who may interact with it and how. Not built.
    • Proposed design points: copying is an explicit action with limits; "rekey the lock" revokes every key at once; offer bearer keys (short-lived, forwardable) and keys bound to a DID; holding a key reveals that a page exists, so unlisted pages stay out of the lobby.
  • Later, keys attach to Groundwork roles for portable delegation. The Groundwork field now called "capabilities" may be renamed "inventory". Deferred (Oct 14): decide once keys exist.
  • Hypothesis (Lyre): legible capability systems start to look like low-code or no-code systems. Croquet and flow-based programming are inspirations as paradigms not rooted in files and IDEs.
  • Ocap norms to learn first come from the Cloudflare OS code (section 5), then Spritely, Agora and MetaMask. Norms noticed so far: one interface per resource, narrowing by holding the specific object rather than passing an id; design the interface first and stop for review before implementing; tile-facing types stay self-contained; every method is classed as an observation or an action; lists of what is reachable are bounded and treated as untrusted; a shared conformance test suite per provider.

5. Outside systems and the gatekeeper host #

Cloudflare OS gatekeepers are connector Workers between an app and an outside service (GitHub, Google, MCP servers, and so on). They are about third-party data and platform integration, not about entry. Source: the announcement and the repository (Apache-2.0), notably .agents/skills/write-gatekeeper/.

  • A gatekeeper has a vendor (the service), a user (one person's connected account, holding the tokens) and an instance (one specific resource).
  • Credentials never reach app code. Reads that return data are logged as observations. Writes with outside effects are queued for approval, and reads then show them as already applied.
  • When something is shared, each new collaborator is checked, through their own connected account, against everything already observed. This is the same question a shared room asks about a tile, so the inspector can show it.
  • Lyre's deployed instance (Cloudflare account, not in this repo): cloudflareos, cloudflareos-backend and gatekeepers for google, github, mcp, mcpportal, context and scheduler, deployed through Cloudflare OS's own configuration starter (Workers last modified Oct 13). Lyre considers them active and ready to test against. The Google one is not configured correctly on the Google side yet. Do not modify them from this repo's work.
  • Gatekeepers are called by a host over Workers service bindings (the vendor and the connected account are plain Workers; only the per-resource gatekeeper is a Durable Object class that the host runs inside its own Durable Object as a facet, per Cloudflare OS's gatekeeper.ts). In Cloudflare OS the host is inside cloudflareos-backend. Fieldwork needs its own host to use them. Decided (Oct 14): a separate Worker in its own directory of this repo, not inside the Fieldwork site Worker, because it holds credentials by reference and the approval queue, can serve more than one app, and makes Fieldwork a plain client over capnweb. Its name, and what happens to gatekeeper/, are undecided (see below). "Overseer" is rejected except when citing upstream; "Concierge" is Groundwork's.
  • Gatekeepers are tied to the venue for now. Tying them to a person's PDS comes later and is out of scope for Fieldwork.
  • First outside gatekeeper (decided): GitHub. Google comes once its configuration is debugged. Cloudflare OS's gatekeeper-kit package (the conformance suite) is worth deploying, but only makes sense once Context, Scheduler and MCP-portal gatekeepers are bridged and Fieldwork is a harness (next point).
  • Fieldwork as a harness (stated goal). Build it from the inside: agents use the space's content-management and webtile-creation affordances to communicate and collaborate with Lyre in it.
  • Open: the name. Lyre leans toward renaming gatekeeper/, the Worker and its Durable Object, but wants the architecture explained first. Renaming changes the deployed Worker's URL and needs a Durable Object migration, and only Lyre deploys from the Cloudflare dashboard. Nothing is renamed until she decides.
  • Data from the atmosphere need not be fetched from a PDS directly; faster, more dependable routes exist.
  • The gatekeeper/ directory here is Fieldwork's own authority host and Tangled connector, written in Cloudflare OS's gatekeeper shape. Under Lyre's rule that nothing is a gatekeeper unless it works with Cloudflare OS, whether it keeps that name is her call.

6. Entry, pages and portals #

  • Entry will rest on atproto spaces, through Groundwork, which builds on the opensocial.groups specification, which builds on atproto's permissioned spaces. Until Groundwork exists, entry uses keys.
  • Always say "atproto space" or "Fieldwork room/page/zone". Never bare "space".
  • A venue has a public lobby, which is essentially its index page. Subpages (and stubs for them) can be any kind of webpage: another Fieldwork page with a different layout, an object graph, a page of people, and so on. Portals lead to other pages, each at its own URL, reached without a page reload and also openable straight from the URL. The venue layout works as the site's information architecture.
  • A placeable portal object creates a new page at a new slug, a spatial wiki. A slug may also hold other content: a markdown file, or an existing record that already has a renderable view, mapped to a URL.
  • Decided (Oct 14): the venue keeps a small index of slugs, first come first served, and hosts can remove or reassign them. For now the page record lives in the creator's own repository. When Groundwork is integrated, creation can be delegated to the venue's repository or to groups that belong to the venue's group; that is later. If a creator leaves or deletes the record, the venue keeps the last saved copy and hosts can re-home the page.
  • Order (decided): the proof object comes first, pages second.
  • Multiple rooms are wanted now. Decided Oct 19: the lobby is the index page; subpages are not limited to rooms. Not built.
  • atproto spaces were opened to alpha on 2026-08-20: breaking changes expected, no backups, no security review. Do not depend on them for anything that must last.

7. The world #

Built Sims-style, inside the world.

  • Walls run at 45-degree angles only: a creative constraint, on purpose. Floors and outdoor areas are kinds of place. Room size is set in the room. Not built.
  • Zones are drawn with shape and line tools (freehand or structured) and stored as vectors, adapting a small existing vector library rather than building one. It must be fun. Not built.
  • Zone objects. An object can declare a zone, so the zone lives and travels with the object. A zone can also group objects and place them into a room. Zones become composable content.
  • Crafting. Zone objects can be merged with other objects at the workbench to make a context that deploys to a space quickly. This is a core affordance and one of the first craftable things. Crafting forms a new atproto record composed of the crafted objects, and it receives capabilities from the component objects.
    • Decided safeguards: a placed object's zone is a request the room can refuse or shrink, and the person sees why in plain words ("This object isn't authorized to do X. Would you like to Y, or cancel placement?"). A crafted object holds only narrowed capabilities and shows a diff first; this is "very Minecraft" and a place for UX experiments that need not expose code.
    • Open, replaced by Lyre's principle: an earlier proposal said the saved record holds a recipe, never authority. Lyre's view is different: if an object can delegate authority, that authority survives crafting; if it cannot, the object either cannot be crafted or loses the capability. This needs more thought because creating roles in Groundwork belongs to the same architecture.
  • Layout language. A legible web-component and CSS markup, shown in a code view, a panel view and the render view. Decided (Oct 14): cells and edges are the canonical model for architecture (walls, doors, floors), zones stay free-form vectors, and the markup shows both. An optional ASCII floor-plan layer may sit on top. Layout code should be extractable as a library. Publish lexicons only after the markup stabilizes.
  • Partly: site/lib/world2d-geometry.js has the projection (isometric 2:1 and a tilting oblique view). There is no rotation, wall tool, zone or layout language yet.

8. The shell #

  • The left bar nests: people, objects, pages and zones as a tree, working as site navigation, a course outline and a window manager (influences: Mozilla Hubs, Spoke, Prezi). Proposed: nest by containment (venue, page, zone, object, surface) with an address for each node. Partly: now.js and outline.js provide today's list (people, things, what is happening).
  • The bottom bar gets a Craft button beside the inventory button (today's UI calls the inventory the Bag). Not built.
  • Changing the view angle on small screens is wanted. Decided (Oct 14): build all four (edge indicators, zoom-out overview, tilt slider, 90-degree rotation), with edge indicators as the default and a toggle for each in the user settings, so Lyre can try them all before choosing. Not built.
  • Experiment: Fieldwork as the shell for a Sandstorm fork, where objects correspond to grains and the left bar is a richer grain navigator.

9. Engines and seams #

Engine swap as a feature is gone, but the lines between shell and engine stay clear so other shells can be integrated, and different rooms may eventually use different engines. The contract is by data (objects, zones, avatars, doors, events, commands) and is checked by tests. The six seams chosen on Oct 9 (presence, shared document, voice, identity, records, sprites) are the presence engine's hooks. The 2D renderer still has no name that avoids implying XR or 3D.

10. Principles from September that still hold #

Kept: your things are yours (they live in your repo); on the page means together; interpret or ignore (every thing has an honest fallback); UX first, records second; pop the hood (source and network visible in the place); phones and laptops are equal; a light core with everything else coming in through one door (defineApp); scoped by default; reuse before inventing; the venue runs its own small server.

Changed: one lobby becomes a public lobby (the index) plus multiple pages; the engine stays untouched went away with the XR Publisher entry; markup is the scene is open (see section 7); "Plug" and "registry" give way to hooks and the inspector. The full September text is in archive/principles-and-architecture-2026-09-29.md.

11. Not now #

Building Specs 1, 2, 3 and 7 as written (they are superseded by this document and live in draft pull requests #19 and #10 and the project's docs); syncing with the 3D entry; building the shell from tiles; a lexicon for layouts; Groundwork integration; the Sandstorm experiment; real atproto spaces. "Not now" means not today. Lyre will get to these when she gets to them, which could be soon.