Parking Lot

Short-lived browser collaboration slots for LLM-owned work.

base: https://parking-lot.tini.works updates: memory default TTL: 10m0s max TTL: 30m0s

What This Service Does

An agent creates a temporary slot, uploads an index.html plus any relative-path assets, sends the public slot URL to a human, and reads back managed feedback as latest state. The public slot behaves like a small short-lived web root: / resolves to index.html, and files can reference each other with relative paths.

Slots are intentionally small and temporary. They are for quick review, comments, annotations, forms, and edit boxes during an agent/human interaction.

Tokens And Safety Rules

TokenWho uses itWhat it can do
create tokenInternal agent/server onlyCreate new slots.
owner_tokenThe requesting LLM/agentUpload files, change visitor policy, read latest state, connect as owner.
share_urlHuman browser linkShort friendly link that opens the slot and stores visitor capability in an HttpOnly cookie.
visitor_tokenLow-level browser/client codeExplicit bearer token for managed visitor writes when not using share_url.

Do not put API tokens in query strings. Send API tokens with Authorization: Bearer .... For humans, prefer share_url; it uses a short #pl=... fragment, resolves through a POST body, and then opens the slot with cookie-backed visitor auth.

Quick Start

Set the base URL and internal create token in the agent environment:

BASE="https://parking-lot.tini.works"
CREATE_TOKEN="internal-create-token"

1. Create A Slot

curl -sS -X POST "$BASE/api/slots" \
  -H "Authorization: Bearer $CREATE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "ttl_seconds": 600,
    "enabled_modes": ["comment", "form", "annotation", "edit_box"]
  }'

The response includes slot_id, owner_token, share_url, visitor_token, public_url, and owner_ws_url. Share share_url with humans.

2. Upload Static Files

SLOT_ID="slot_..."
OWNER_TOKEN="..."

curl -sS -X PUT "$BASE/api/slots/$SLOT_ID/files/index.html" \
  -H "Authorization: Bearer $OWNER_TOKEN" \
  -H "Content-Type: text/html" \
  --data-binary @index.html

curl -sS -X PUT "$BASE/api/slots/$SLOT_ID/files/assets/app.css" \
  -H "Authorization: Bearer $OWNER_TOKEN" \
  -H "Content-Type: text/css" \
  --data-binary @assets/app.css

The public slot URL serves index.html at the slot root and other files by relative path.

3. Share The Friendly URL

echo "$SHARE_URL"
# Example: https://parking-lot.tini.works/#pl=amber-bridge-9rm6kp

Keep public_url only for low-level clients that need the explicit /s/slot_id/#v=visitor_token form.

4. Visitor Writes Feedback

Visitors can write only through modes enabled by the owner policy. Pages opened from share_url can rely on the visitor cookie and omit the authorization header:

await fetch("/api/slots/" + slotId + "/visitor-updates?mode=comment&target=review", {
  method: "POST",
  headers: {"Content-Type": "application/json"},
  body: JSON.stringify({"text": "This part is unclear.", "author": "human"})
})

Low-level clients can still use the explicit visitor token:

VISITOR_TOKEN="..."

curl -sS -X POST "$BASE/api/slots/$SLOT_ID/visitor-updates?mode=comment&target=review" \
  -H "Authorization: Bearer $VISITOR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"text":"This part is unclear.","author":"human"}'

5. Owner Reads Latest State

curl -sS "$BASE/api/slots/$SLOT_ID/latest" \
  -H "Authorization: Bearer $OWNER_TOKEN"

Owner Listen Loop

To steer from human feedback, the owner agent should connect to owner_ws_url before or immediately after sharing the slot. The first message is hello with current latest and policy. Every accepted visitor or owner change is broadcast as an update message.

OWNER_WS_URL="wss://.../api/slots/$SLOT_ID/ws?role=owner"

websocat -H "Authorization: Bearer $OWNER_TOKEN" "$OWNER_WS_URL"

Owner message shapes:

{"type":"hello","latest":{...},"policy":{...}}
{"type":"update","event":{"id":1,"actor":"visitor","mode":"comment","target":"review","payload":{...}},"latest":{...}}
{"type":"policy_update","policy":{...}}

The steering loop is:

  1. Connect as owner and keep the socket open.
  2. On hello, seed local state from latest.
  3. On update, inspect event.actor, mode, target, payload, and latest.
  4. Respond by uploading changed files, patching policy, or sending an owner update.
  5. If WebSocket is unavailable, poll GET /api/slots/$SLOT_ID/latest and track the highest events[].id already handled.

Owners can send live state back to the browser on the same socket:

{"type":"owner_update","target":"status","payload":{"message":"Updated from feedback"}}

Browser Realtime Helper

Uploaded pages can include the built-in helper instead of hand-writing visitor WebSocket code. It connects as the visitor, sends managed updates, dispatches DOM events, and renders owner updates into elements marked with data-pl-owner-target.

<script src="/parking-lot-client.js"></script>

<div data-pl-owner-target="status">Waiting for owner...</div>
<button id="send">Send note</button>

<script>
ParkingLot.on("update", ({event, latest}) => {
  console.log("slot update", event, latest)
})

document.querySelector("#send").onclick = () => {
  ParkingLot.sendVisitorUpdate("comment", "review", {
    text: "Please revise this part",
    author: "human"
  })
}
</script>

Managed Visitor Modes

ModePayloadLatest-state behavior
comment{"text":"...", "author":"..."}Appends a comment.
form{"fields":{"name":"value"}}Replaces form state for the target.
annotation{"anchor":"...", "data":{...}}Appends an annotation.
edit_box{"text":"..."}Replaces edit-box state for the target.

Changing Visitor Policy

curl -sS -X PATCH "$BASE/api/slots/$SLOT_ID/policy" \
  -H "Authorization: Bearer $OWNER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"enabled_modes":["comment","annotation"]}'

Future visitor writes are checked against the latest policy.

Raw WebSocket Shape

Owners connect with an authorization header:

GET /api/slots/$SLOT_ID/ws?role=owner
Authorization: Bearer $OWNER_TOKEN

Visitors connect to /api/slots/$SLOT_ID/ws?role=visitor, then send the first message:

{"type":"auth","token":"visitor-token"}

After auth, visitors send:

{"type":"visitor_update","mode":"comment","target":"review","payload":{"text":"Looks good"}}

Owners can send live owner state:

{"type":"owner_update","target":"status","payload":{"message":"Updated the diagram"}}

Limits And Expiry

Operational Notes