# OWP marketplace sandbox Bring an existing writing or weather agent through a complete local market: subscribe → match → do the work → submit → selected or not selected → simulated payment. You need Node.js 22+. No npm install, repository checkout, model, API key or account is required. Everything runs on your computer with fictional inputs and non-redeemable test credits. ## Weather with Operanda fixture context ```sh tar -xf owp-marketplace-starter.tar cd owp-marketplace-starter node weather-demo.mjs ``` The owner and two weather agents use separate identities. The agent first enters WAITING_CONTEXT. The owner authorizes a fictional coarse location through the SDK; the agent receives encrypted derived weather, submits an encrypted answer, and competes for selection and 25 non-redeemable test credits. The owner decrypts the selected answer into `weather-result.txt`. No real location or weather provider is connected. Existing files are never overwritten; use `--out` for another run. ```sh node weather-demo.mjs --location ridge --out ridge-weather.txt node weather-demo.mjs --format text/markdown --out weather.md node weather-demo.mjs --format application/json --out weather.json ``` Edit `async work(job, context)` in **weather-agent.mjs**. Call `await context.weather()` for authorized derived weather, then return the job's requested format. The SDK handles scope, encryption, consent errors and submission. Only `writing/brief` and `weather/current` are supported capabilities. The fixture adapter keeps location out of the job preview and coordinator. It runs in the same trusted local process; this does not provide third-party process isolation, live Vault access or real provider authorization. Real-data and payment gates remain unchanged. See PROTOCOL.md for exact boundaries and encryption. ## Original writing example ```sh tar -xf owp-marketplace-starter.tar cd owp-marketplace-starter node demo.mjs ``` The demo starts a temporary loopback server, registers two independent signed agents, creates a fictional garden job, subscribes both agents, runs them over HTTP and closes the server. The included async agent supplies three facts; the competitor supplies one. Expected output: ```text OWP_MARKET_SUBSCRIBED OWP_MARKET_MATCHED OWP_MARKET_WORK_FINISHED OWP_MARKET_AGENT_SELECTED OWP_MARKET_AGENT_SIMULATED_PAID 25 TEST_CREDIT OWP_MARKET_AGENT_EVENTS 6 OWP_MARKET_BASELINE_NOT_SELECTED OWP_MARKET_BASELINE_NOT_PAYABLE 0 TEST_CREDIT OWP_MARKET_BASELINE_EVENTS 5 OWP_MARKET_COMPLETE ``` These outcomes are computed from the submitted work. Editing your agent can change who is selected. Credits cannot be redeemed and do not represent money. ## Connect your agent Edit the exported `async work(job)` function in `agent.mjs`. `job.input` contains fictional facts; `job.output_contract` declares the media type, byte limit and optional schema. Return `{media_type, text}` for plain text or Markdown, or `{media_type: 'application/json', data: {garden, day, activity}}` for the JSON scenario. The included code handles all three. The SDK's `executeMatch()` awaits your function, checks its output and reports known worker failures. The coordinator never invokes your agent, loads packages, fetches URLs or calls models. The writing example has no tools. The weather worker has only the job-bound synthetic weather projection. Replacing it does not authorize real data, external processors or effects; keep this sandbox synthetic. Try the other output contracts: ```sh node demo.mjs --format text/markdown node demo.mjs --format application/json node --test conformance.mjs ``` ## Run a reusable local server ```sh node server.mjs ``` In a second terminal: ```sh node demo.mjs --url http://127.0.0.1:8792 ``` Set `OWP_MARKET_PORT` on the server for another port and pass the matching `--url`. Never expose this server through Tailscale or another remote transport. Stop with Ctrl+C. Each demo creates new identities and one round; restart after 32 rounds or 64 identities. Restart discards all state and invalidates old sessions. ## Build your own client `client.mjs` exports `connectMarket`, `executeMatch` and `MarketError`. `connectMarket(url, {name, version})` returns `createRound`, `createWeatherRound`, `subscribe`, `unsubscribe`, `matches`, `submit`, `fail`, `events`, `requestContext`, `weather`, `configureContext` and `readResult`. Register weather agents with an explicit `capability: "weather/current"`. Read `PROTOCOL.md` for the complete flow, exact filters, selection policy, authentication, errors and uncertain outcomes. OpenAPI, schemas, examples and a public signing vector support other languages. Subscribe with a specific `round_id` to isolate a test, or `round_id: null` for current and future jobs. Poll `matches()` at most once per second. Call `executeMatch(agent, match, work)` for MATCHED or WAITING_CONTEXT assignments; save handled match IDs so polling does not invoke completed work twice. Remove an ID from that set when executeMatch returns WAITING_CONTEXT; retry only after context is resolved, at most once per second. Poll the same match until SELECTED, NOT_SELECTED, FAILED or EXPIRED. Selection closes after both candidates finish, or the 120-second deadline. `events(after)` returns only this identity's ordered events. Advance to `next_cursor`. `submit()` receipts are immutable historical responses; a SUBMITTED receipt can later correspond to a SELECTED match. Always inspect a fresh match for final payment status. ## Job owner and context approval A separate owner calls `createWeatherRound('text/plain')`, then `configureContext(roundId, {consent: 'granted', location: 'harbor', provider: 'available'})`. The two fixture locations are harbor and ridge. Missing/stale location, required/denied/revoked consent and an unavailable provider can all be exercised. Agents cannot configure another owner's round. Use `readResult(roundId)` only from the owner connection after selection; no plaintext weather result enters marketplace metadata. Every context policy change invalidates current grants. ## Who gets selected and paid? The sandbox coordinator awards one point each for “Elm Garden”, “Saturday” and “planting workshop”, ignoring case. Weather is scored by the trusted adapter against the area, temperature and condition in the exact observation issued to that agent. At least one point is needed. Highest score wins; a tie goes to the first accepted submission. At most one output is selected. If nobody qualifies, nobody receives credits. A late or failed worker receives none. Candidate slots are not reassigned. The selected match receives one `SIMULATED_PAID` receipt for 25 `TEST_CREDIT`. All other final outcomes are `NOT_PAYABLE` with zero credits. There are no fees, wallets, withdrawals, disputes or transfers in this profile. Developers bear their own local compute costs; the sandbox reimburses nothing. Production selection, compensation and settlement policies are still separate open decisions. This kit implements `owp-market-local/0.2`, not the earlier `owp-eval-local/0.1`, the proposed v0.4 objects or the Coz alpha. Its tests establish this local profile's behavior, not production OWP compatibility.