Changelog
What we've added and improved in the shop, your account, and personal-agent shopping. This history brings together completed Codex and Cursor work, checked against the project's code. Dates record the work, not guaranteed production release dates.
262 updates · 10–12 October 2026
Recognize metadata-document agents in Auth0
Deployed agent registration and recovery; native login and MCP delegation acceptance remain pending.
Agents
CIMD clients receive a stable Auth0 Agent
An authenticated agent now receives its Auth0 Agent before a customer needs to exist, and its own application is linked to that identity. Public clients register after a successful customer grant with claimed provenance. New grants prefer CIMD over Web Bot Auth while existing authorizations keep their original provenance. Replacing an application preserves its Agent identity. Confirmed application links reuse the existing status cache, interrupted Agent purges can finish on retry, duplicate Agents can be removed while safely preserving a pending survivor or another registered identity, with durable deletion fences also protecting clients whose identity is still empty, and operators can explicitly reconcile pending Agent creation after a complete ownership check and grace period. Poppy registration handles concurrent requests, bounded recovery, quotas and deleted identities without silently issuing unidentified tokens. Auth0-native CIMD linking is enabled through a dedicated post-login integration; its first login can still issue an unlinked token. The Worker, Actions and resource claim profiles are deployed together. End-to-end native login and MCP delegation acceptance remain pending.
Move the shop to tarnandtimber.com
Infrastructure moves to Tarn & Timber names, domains and a new Auth0 tenant.
Agents
Tarn & Timber now speaks Personal Agent Protocol draft 0.2
The discovery document and the shared agent-side validator now declare and require draft 0.2, and nothing is kept for 0.1. The document says `protocol_version: "0.2"`. The validator in `packages/protocol` accepts only the exact versions it supports (before 1.0 every minor release can break) instead of any 0.x, compares `organization.domain` with the host that actually served the document after redirects rather than the requested host, and compares domains ignoring case only, so `www.` is no longer treated as the apex. It warns when the document came from a different host than the one asked for, treats `feedback.endpoint` like the other endpoints (HTTPS, and `auth` is required with it), and has a check that a Device Sign-In link points at the issuer, a `poppy_domains` entry or a subdomain of either. The Auth0 proxy no longer lists `dpop_signing_alg_values_supported` in the issuer metadata it builds on top of Auth0's, because that issuer also serves ordinary Auth0 clients; it still accepts ES256 DPoP proofs, and Poppy-only (standalone) metadata still lists the accepted algorithms. The test agent now uses a separate DPoP key for each User ID in Session-assertion requests and takes the token scheme from the token response. The compliance page is rewritten against the 0.2 sections, recording that Mediated Sign-In, Feedback and the Operations extension are not offered and that our MCP server accepts Bearer tokens only (draft 0.2 recommends DPoP for new MCP servers, so this is a recorded deviation). The vendored protocol text was refreshed to 0.2. The browser end-to-end suites have not been run.
Agents
Agents can no longer sign in with your email and password
Tarn & Timber no longer offers Mediated Sign-In, the draft 0.2 option where an agent collects a person's credentials and passes them on. Our Personal Agent Protocol document no longer lists `auth.mediated`, `POST /poppy/sign-in` and `POST /poppy/sign-in/{id}` are gone, and the Auth0 proxy accepts no username, password or one-time code from an agent. Agents sign in with Direct Sign-In (the person logs in on our site) or Device Sign-In (a code approved on another device). The Auth0 proxy's Auth0 applications lose the password and multi-factor grant types (existing per-agent applications on their next update), the password-grant rate limits and the database-connection setting are removed, and the Post-Login Action ignores agent context sent in a token request body. The Connected agents page no longer has a label for this sign-in type. People still sign in with their password and any multi-factor prompt at Universal Login. The shared protocol package still reads `auth.mediated` for other Companies.
Agents
Agent metadata, key sets and addresses are checked strictly, as draft 0.2 requires
The Auth0 proxy now refuses a Personal Agent's metadata document with invalid_client when its `logo_uri` or `client_uri` is not an HTTPS URL, where it used to ignore the field. Local development (`allowInsecure`) now accepts http logo and home-page URLs on localhost and other private hosts; before, http ones were dropped in every mode. A malformed entry in the document's `extensions` (a missing or non-string `version`, or a value that is not an object) is now dropped and ignored instead of rejecting the agent, since the Auth0 proxy supports no extension. An agent's key set must hold only public keys: if any key carries a private or symmetric member (`d`, `p`, `q`, `dp`, `dq`, `qi`, `oth`, `k`), the whole set is refused with invalid_client instead of silently skipping the key. A DPoP proof whose `jwk` header carries any of those members is refused too (it already refused `d`, `p`, `q` and `k`). The check that stops the Auth0 proxy fetching loopback, private or link-local addresses now covers the whole IPv6 link-local range (fe80::/10), IPv6 unique-local addresses, IPv6 multicast, names with a trailing dot (`localhost.`), and IPv4-mapped, IPv4-compatible and NAT64 IPv6 forms of blocked IPv4 addresses (such as `::ffff:7f00:1`), which are now judged by the IPv4 address inside, so a mapped public address is no longer refused (the old check blocked every `::ffff:` address); it also refuses text it cannot read as an address; resolving a public name to a private address is still left to the Workers runtime. Configuration validation now applies the rule that an MCP server's resource equals its url to direct MCP resources too, so a direct MCP resource must be written without a path or trailing slash.
Agents
Browser sign-in state is always current, and MCP and failed sign-ins behave as draft 0.2 requires
A Personal Agent's browser cookie is now checked with the Auth0 proxy on every request, including page views, so signing in, signing out or changing scopes shows on the next request instead of up to five seconds later. Agent sign-in tokens keep their short cache. A failed replacement sign-in (the new grant could not be stored) now leaves the Session signed in to its previous grant, scopes and Account Token, where it used to be signed out, and a refused sign-in no longer leaves the account pointing at the Session. A Session started by an ordinary MCP client through MCP's own authorization flow no longer pretends to have a Poppy User ID: it can't join a browser, and it ends when its sign-in is revoked or expires instead of continuing signed out. The browser-session endpoint answers its rate limit with HTTP 429, `{"error":"rate_limited"}` and `Retry-After`; failed assertion checks keep the HTML error page.
Agents
Conversations survive sign-out and reject malformed requests, as draft 0.2 requires
Signing out, or disconnecting every grant, no longer closes a Personal Agent's conversations. The account and credentials are still stripped from the stored conversation, so the Company Agent and staff stop acting with them and open event streams re-check access, but the conversation keeps its owner and history. The same client and User get 403 sign_in_required until they sign in to that account again, and then carry on where they left off; conversations that were explicitly closed stay closed. Deleting the account still closes and erases its conversations, and an email change in progress still closes them. Requests are also stricter: a `wait` that is not a whole number of seconds (empty, `abc`, `-5`, `1.5`) and a conversation ID that is not 1 to 256 characters of letters, digits, underscore and hyphen (also a `parent_conversation_id`) are refused with 400 invalid_request before a message is accepted, where `wait` used to be quietly ignored and a bad ID answered 404. Very long `wait` values are still capped at 30 seconds, and a well-formed unknown ID is still 404. The sign-out notification endpoint now reports `notified` instead of `closed`.
Agents
Agent assertions must name the issuer, as draft 0.2 requires
The Auth0 proxy now follows Poppy draft 0.2 for the JWTs a Personal Agent signs. A client assertion's `aud` must be the issuer exactly as published (https://login.tarnandtimber.com/, trailing slash included) as a single string; the token endpoint, any other endpoint URL and arrays are refused with invalid_client, at the device, token and revocation endpoints alike. A Session assertion's `aud` must likewise be the issuer, not the token endpoint, or the grant fails with invalid_grant. Client and Session assertions may now live up to 300 seconds (browser assertions stay at 60) and a browser assertion's `aud` must be a single string. Agents that sign the token endpoint as `aud` stop working. Hosts kept for the old issuer accept only that old issuer, not their token endpoint. The test and smoke agents in the repository now sign the issuer and generate 128-bit `jti` values.
Agents
Personal agents find the shop at its own domain
The shop now serves its Personal Agent Protocol discovery document (`/.well-known/poppy.json`) itself with a 200, instead of redirecting agents to the sign-in host. Draft 0.2 of the protocol says a document speaks for the host that serves it after any redirects, so the old redirect made the document claim tarnandtimber.com while login.tarnandtimber.com served it. The shop fetches the Auth0 proxy's document for its own host with the service secret, checks that it names that host, and refuses (502) anything else. The Auth0 proxy's own `/.well-known/poppy.json` no longer names the Company domain from its host: without the service secret it redirects to the Company domain, unless its own host is a Company domain (local development), and a `?domain=` request without the secret gets 404 so an older shop's redirect can't loop. The shop no longer strips a leading `www.` when it builds the document; instead, Alchemy puts `www.tarnandtimber.com` on the shop Worker as a 301 redirect to tarnandtimber.com at the edge, path and query kept, and both the composed stack and `pnpm infra:plan`/`infra:deploy` stop before deploying anything when the shop host isn't listed in the Auth0 proxy's `poppyDomains`.
Agents
See and delete agents in the backoffice
The backoffice has an Agents page listing every Auth0 Agent with the Poppy agent applications that sign with it and the invited agents (such as a Grok account) tied to it, plus anything linked to no Agent. Auditors can delete an Agent, or one application, after typing a confirmation: the shop disables the invited agents, ends their sessions, deletes their Auth0 users and removes their identities, connections, delegated sessions and the invitations of that Agent's platforms; the Auth0 proxy then revokes every customer's grant for the agent (including older grants, found by sweeping accounts) and ends the sessions signed in with them, deletes the Auth0 application and the Agent, and wipes what it held for the agent. Deleting is a clean slate: the agent can connect again and starts fresh, and a blocked agent loses its block. Large deletions finish over several runs, offered as "Continue deleting". The Auth0 proxy's operator API takes its own secret, `PROXY_AUTH0_OPERATOR_SECRET`, held only by the Shop API, and its Management application gains `delete:clients`, `delete:agents` and `read:users`; the backoffice gains the `ops:agents:delete` scope. Local work; not deployed.
Behind the scenes
Filter backoffice Logs by level, showing Info and above by default
Every log event now has a stored level (debug, info, warn or error), set when it is recorded from its outcome and, for neutral observations, its name; existing events were given a level by the same rules. The Logs page and each customer Logs tab have a Level control meaning this level and above. It starts at Info, so routine debug events are hidden and a small note says so with a Show all link, and ?level=debug shows everything. Changing the level restarts paging, the Open in Logs link carries it, and the Shop API accepts it as a minLevel filter. The level has no index of its own, so recording an event costs no extra writes. Local work, not deployed.
Behind the scenes
Auth0 proxy events use the same names as every other service
The Auth0 proxy put poppy. in front of every event it recorded, including plain request handling, so the backoffice Logs showed poppy.request.completed and poppy.provider.http.completed next to the request.completed and provider.http.completed of the other services. Its generic events (request.received, request.completed, request.credentials, provider calls, executions, token.observed, token.used and audit.gap) now keep the shared name, and the Producer column still shows they came from the Auth0 proxy. Events from the Poppy flows it runs, such as agents, device approval, grants, refresh tokens, sessions and step-up, are now named auth0.poppy.*, for example auth0.poppy.device.approved. The Shop API accepts only those names from the proxy. Events stored before this change keep their old poppy. names. Deployed on 20 October 2026.
Behind the scenes
Auth0 Management API logs show their route in the Logs
Auth0 tenant logs that record an HTTP call now fill the Route column in the backoffice Logs with the method and path Auth0 recorded, and keep the response status. That covers Management API reads and changes (mgmt_api_read, sapi, fapi), rate-limit warnings, Guardian settings changes and endpoint logs such as a refresh token revocation. The query string is dropped, so query values stay out, and paths longer than 512 characters are cut short. Request and response bodies are still not kept on the event. Logs without a request, such as logins and client-credentials exchanges, have no route. Logs stored before this change keep their empty route. Deployed on 20 October 2026.
Behind the scenes
Give every backoffice log a readable message
Every row in the backoffice Logs table, and in a customer's Logs tab, now leads with a one-sentence Message such as "GET /v1/cart → 200 · guest", "Called Auth0 POST /oauth/token → 200 in 135 ms", "Agent is polling for device approval (authorization_pending)" or "Auth0: user updated"; the event name sits beneath it in small monospace text, the full message is the tooltip, and the event sheet's title is the message with the event name and level under it. The wording is built in the browser from the event's name, outcome, route, status and recorded actor (a guest, a user ID, an agent or an operator), never from tokens or header values, and every event family, including events no one wrote a sentence for, gets one. Auth0 tenant logs now read as their Auth0 type (for example "Exchanged authorization code for tokens"), and the receiver keeps Auth0's own description and the application name from the log, so a log reads "Auth0: Successful exchange of Client Credentials for Access Token · Tarn & Timber Storefront". Verified locally; not deployed, and the Shop API must be deployed for new descriptions to be stored.
Behind the scenes
Read the raw Auth0 payload of a log and open the log sheet wider
For logs that came from Auth0 (tenant logs and lifecycle events) and for Auth0 proxy events, the Shop API now keeps the original item it received, only while demo credential capture is on at the collector, and an auditor can read it in a new Raw JSON tab of the log sheet. The tab loads only when opened, pretty-prints the JSON with a search box and a copy button, and clears when the sheet closes or changes event. Client secrets, passwords, API keys, Basic and bearer authorization values and any configured service secret are replaced by a short fingerprint before storage, a payload over 64 KiB is not kept, and the proxy's headers and tokens stay behind their own tabs. Each read records an audit.raw_released event in the API and an audit.raw_viewed entry in the operator audit log first. The raw payloads count towards the log storage budgets and are deleted with their event. The log sheet is also wider on desktop, up to 1,400 pixels or 92% of the screen, so JSON and tokens need less scrolling. Demo only and local; not deployed, and the database migration 20261020120000_diagnostic_raw_payload must be applied with the Shop API.
Behind the scenes
Let logs use their full 1 GiB allocation
Logs kept 30 days of events but were capped at 100 MiB of storage, a fifth of their configured 1 GiB allocation in the shop database. On 20 October that cap would have been reached within hours, after which new events are dropped. Logs may now use the whole configured allocation (AUDIT_DATABASE_CAPACITY_BYTES, 1 GiB in production). The 30-day retention and the 100,000-event and per-minute ceilings stay. Verified locally; not yet deployed.
Behind the scenes
Received requests show their route in the Logs
The Route column in the backoffice Logs was empty for every request.received event, and for both request events from the storefront, the MCP server, the backoffice and the Auth0 proxy, because they only recorded the host and, for most, the method. They now record the request path, and the Auth0 proxy also records the method. The query string is dropped, so OAuth codes and other query values stay out, and paths longer than 512 characters are cut short so the event is still kept. A Shop API request.completed still shows the matched route template, such as /v1/cart/items, while its request.received shows the actual path. Events recorded before this change keep their empty route. Deployed on 20 October 2026.
Behind the scenes
The Auth0 proxy rate limiter and replay cache no longer fill the Logs
Two Auth0 proxy stores that only keep internal records wrote an execution started and an execution completed event to the Logs for every call: the rate limiter, on every check and every cleanup alarm, and the replay cache, which remembers each DPoP proof and signed assertion ID so a captured proof cannot be used twice, on every claim and every five-minute purge alarm. That was 152 of the 751 events in production, about 2.2 MB, and none of them added anything to the request they belonged to. Both now record nothing. The requests they guard are still logged, a replayed proof or assertion is still refused and that request is logged as denied, and the proxy's other Durable Objects are logged as before. Limits and replay protection work exactly as before. Deployed on 20 October 2026.
Behind the scenes
Device sign-in polling is no longer logged as denied
While an agent waits for someone to approve a device sign-in, it keeps asking the Auth0 proxy's token endpoint, which answers HTTP 400 authorization_pending as the device-flow standard requires. Each of those answers was logged as a denied request. They are now logged as observed, with their error code, along with slow_down and DPoP nonce retries. Real refusals stay denied. Verified locally; not yet deployed.
Behind the scenes
Rename Diagnostic events to Logs in the backoffice and add a Logs tab to each customer
The backoffice page that was called Diagnostic events is now Logs, at /logs: the sidebar, heading, breadcrumb, document title and Stats copy say Logs, and the old /diagnostics address is gone. Filters, the ?tab=stats link and every other search parameter work as before, and a new User ID filter finds one user under any issuer. Auditors also get a Logs tab right of Activity on every customer page (?tab=logs). It lists every log event about that customer, newest first, in the same table, event sheet, j and k navigation and Newer/Older paging as the Logs page, without the redundant User column, and an Open in Logs link continues the same filter there. Viewers do not see the tab and a bookmarked link shows Orders for them; the read is the Logs page's own auditor-only, audited one. To find a user under every issuer, the Shop API diagnostics list accepts a new read-only actorSubject filter that matches an actor reference with that value in any namespace; when legacy actor lookup is unavailable the tab shows the API's message instead of an empty list. Verified locally; not deployed, and the Shop API must be deployed before the backoffice tab returns results.
Agents
Continue from the device code page again
Entering the code from a Personal Agent at login.tarnandtimber.com/device and pressing Continue always ended on "Request blocked: This form must be submitted from this site." The page hides its address from the sites it links to, so browsers sent its own form with an empty origin, which the Auth0 proxy treated as another site. The proxy now asks the browser's own same-site signal first and only falls back to the origin for browsers without it; posts from other sites are still refused. The agent account invitation and delegation buttons had the same check and are fixed the same way. Deployed on 20 October.
Agents
Show the Auth0 proxy pages in the login design
The Auth0 proxy's own pages (the device code page, connected agents, you're all set, declined and error pages) used a plain grey card with a blue button. They now use the sign-in screens' layout: the photo panel with the Tarn & Timber wordmark and brand story, the linen form card, Fraunces headings, Manrope text and forest buttons, with the compact logo header on phones and links back to the shop and its privacy page. They load the pinned login release's stylesheets and fonts, checked by integrity hash, and its first photo; the deploy fills this in from the release, so a new login release restyles them on the next deploy. The pages still run no scripts, so the photo does not rotate. Deployed on 20 October.
Agents
Let an agent reconnect after its Auth0 application was deleted
Deleting a Personal Agent's Auth0 application used to block that agent for good: the Auth0 proxy kept treating the missing application as blocked and never made a new one. Now a deleted application signs every User out of that agent at the next status check, because grants issued under it no longer count for resource requests, session introspection or browser handover. The agent itself is not blocked, and its next sign-in creates a fresh application with the current connections. To keep an agent out, set `poppy_status` to `blocked` on its application or add it to `clients.block`. An earlier revision that still blocked the agent was deployed on 20 October; this one is local and not deployed.
Account
Offer the same sign-in options in agents as on the shop
Signing in through a Poppy agent showed fewer options than the webshop, because the Auth0 proxy only enabled the shop's database connection on each agent's Auth0 application and never Google. Terraform now adopts the tenant's own `Username-Password-Authentication` and `google-oauth2` connections, enables both for the website, the Auth0 proxy and personal-agent owners, and makes both domain-level so third-party MCP clients see them too. The Auth0 proxy enables both on every per-agent application (`AUTH0_GOOGLE_CONNECTION_ID` joins `AUTH0_CONNECTION_ID`), including existing ones on their next sign-in. The separate `Poppy-Users` connection is removed, which deletes the accounts in it; people sign up again in `Username-Password-Authentication`. Google still uses Auth0's development keys. Local work: the next `pnpm infra:deploy` applies it; not deployed.
Account
Show a recovery page when a sign-in or identity check is refused
When Auth0 sends a sign-in back with an error, such as an account security Action refusing an identity check, or the sign-in transaction has expired, the storefront now shows a "Sign-in wasn't completed" page in the login design with a Try signing in again button, instead of a raw 500 error. The page never repeats the provider's error text, sets no session and is not cached; the existing check-your-email page for unverified addresses is unchanged. Failures after Auth0 accepted the sign-in keep their current handling. Verified locally; not deployed.
Agents
Ask for a two-step verification method before an identity check that needs one
Accounts that sign in with Google or another social provider confirm sensitive changes, such as inviting an agent, by completing an existing two-step verification method. If the account had none, the shop still sent the person to sign in again, and Auth0 then refused with "Account security verification could not be completed." Now the shop checks the registered methods first and, if none can be challenged (passkeys and recovery codes do not count), stops with "Add a two-step verification method on the Security page before continuing." Accounts with a password are unchanged. Verified locally; not deployed.
Behind the scenes
Delete every diagnostic event from the backoffice Stats tab
Auditors get a Delete all events button on the Diagnostic events Stats tab. Its confirmation says that every stored event, reference, header snapshot and credential attachment of this environment is removed and cannot be undone, and the button stays disabled until you type delete. The Shop API deletes in batches of 500 rows, restarts the collector's retained-event, storage and per-minute counters and its coverage window, and records one value-free audit.diagnostics_purged event with the deleted counts, so the purge itself stays visible. The backoffice also clears the shared counter-reset baselines and logs diagnostics.purge in the operator audit log. The Shop API endpoint (DELETE /v1/ops/diagnostics) needs the auditor role and a new narrow ops:diagnostics:purge scope, which the backoffice's machine client is now granted next to ops:read. Verified locally; not deployed, so the Auth0 Terraform grant must be applied before the API and backoffice deploy.
Behind the scenes
Write fewer database rows for each diagnostic event
Production D1 insights showed about 8.8 rows written for every diagnostic event, because each event was also written to a dozen indexes. A new migration drops the origin, outcome, category, ASN and country indexes, which are low-cardinality or sparse. Searches on those filters now walk the time index inside the 30-day window under the page limit, and the indexes for time, service, event name, action ID, direct and provider IP, exact references and the sparse legacy-actor check stay, so IP lookups are unchanged. By count that is about 5.5 rows per event instead of 8.8 (an estimate: the dropped ASN and country indexes only wrote when the request had them). On the 100,000-event local fixture the worst case, a category filter whose only match is the oldest event, takes about 0.8 s instead of under 1 ms; ordinary filtered pages stay in milliseconds. Verified locally; not deployed, and the migration must be applied with the Shop API.
Behind the scenes
See which domain each diagnostic event happened on, and open events from their row
The backoffice Diagnostic events table has a Domain column, and the event sheet shows it in the overview. Application requests, Shop API provider calls (the fetched URL's host), Auth0 tenant logs (the log's own hostname) and the Auth0 proxy's request record the host they saw, hostname only, lowercase and at most 253 characters. Events that recorded none show the configured host of their service instead (storefront, API, MCP, backoffice, Auth0 custom domain or tenant domain) in muted text with an inferred-from-configuration tooltip and (inferred) in the sheet; these hosts are new non-secret backoffice settings wired from the stack. The View event column is gone: each row is now the control, focusable with a visible focus ring and named 'View event <name> at <time>', opening with a click, Enter or Space. The j and k keys move focus between rows, and focus returns to the row when the sheet closes. Verified locally; not deployed. Events recorded before this change have no host, so they show inferred domains.
Behind the scenes
See every token, code and assertion that travels to or from Auth0 in the debugger
With demo credential capture on, the Diagnostic events Tokens tab now lists every credential an observed call carried, not only access and ID tokens: refresh tokens, authorization and device codes, client assertions, DPoP proofs, subject and actor tokens, MFA and one-time codes, logout tokens and Poppy assertions, each with its direction (sent or received), place and field, and up to 16 per event. They come from calls to Auth0 (request bodies and JSON responses, read from clones so the caller is untouched), incoming requests to the Shop API, the storefront back-channel logout, and the Auth0 proxy's synthetic stream, which also reports what Poppy agents send it and the tokens it returns. Reveal and copy stay explicit and audited. Client secrets, API keys and service secrets are never stored: the tab shows only their method, a short fingerprint and last four characters, and the 2 KiB header capture now omits them. Local work, not deployed; the proxy and the API both need the capture flag.
Behind the scenes
Keep operator activity out of diagnostic events
Diagnostic events no longer record the backoffice's own work: operator sign-ins, page loads, Shop API operator reads and the Auth0 calls behind them. The operator audit log already has that history, and dropping it leaves the diagnostic storage budget to customer, agent and provider activity. Refused operator requests (401 or 403) and raw credential reveals are still recorded. Events made for an anonymous storefront visitor, such as adding to a cart as a guest, now show Guest in the User column. Verified locally; not yet deployed.
Behind the scenes
Read diagnostic events at a glance and check collector health on its own tab
The backoffice Diagnostic events page now uses the full width and shows times as 'time ago' labels with the exact Brussels time on hover. New User, Agent and Route columns show the email (else the name, else the user ID) of the person an event is about, the agent's recorded ID, and the request method and route; the Action ID and Source columns are gone from the table but stay in the sheet, which is now much wider. Names come from one batched, audited, read-only lookup per page (the API's user search accepts up to 50 user IDs) and fall back to IDs if it fails. Collector and source health moved to a bookmarkable Stats tab with status badges, retention and per-minute budget meters. An auditor can reset the counters after confirming: the page then shows counts since the reset, shared with other auditors and recorded in the operator audit log, while retained events are untouched. Local work; needs the new backoffice migration and an API deploy.
Behind the scenes
Keep the Auth0 log stream within its delivery deadline
Auth0 cancels a log stream delivery after about five seconds. The Shop API stored each log in a batch one after another, with a separate health update for each, so a backlog of about 100 logs took far longer and was cancelled on every retry. No tenant log after 08:34 on 20 October was stored. The API now writes a batch's logs concurrently and updates the stream's health once per batch. Writes stay idempotent, so a cancelled, partly stored batch is still safe to retry. Deployed on 20 October, after which the backlog cleared.
Behind the scenes
Scan and step through diagnostic events faster
The backoffice Diagnostic events table now has a Level column that shows each event as a debug, info, warn or error badge. The level is worked out when the page is shown, from the recorded outcome (and, for neutral observations, the end of the event name such as failed or expired); it is not stored. Event names use the code font, clicking anywhere on a row opens its detail sheet unless you are selecting text, and the j and k keys move to the next (older) or previous event: with the sheet closed they move the focused row, with it open they switch the sheet exactly like choosing another row, so revealed headers and tokens are cleared. Keys are ignored while typing and at the edge of the page. Deployed on 20 October.
Account
Security settings work again
On the account Security page, the list of registered sign-in methods failed to load in production, so methods could not be removed. Regenerating recovery codes also failed. The storefront never received its Shop Management scopes and now takes them from the Shop API. The Shop Management application can now read the tenant's MFA policy and connection, which every step-up checks before removing a method or issuing recovery codes. Recovery codes are also enabled as an MFA factor on the tenant. Deployed on 20 October; not yet exercised on the live Security page.
Behind the scenes
Record sign-in and token diagnostics in production
Auditors' Diagnostic events in the backoffice now fill up in production instead of showing every source as disabled. The storefront, MCP server and backoffice report sign-in started and completed, callbacks, refreshes, step-up, logout and Auth0 HTTP calls. The tenant's own Auth0 logs arrive through a log stream: every log type (logins, code, refresh-token and client-credential exchanges, Management calls, blocks and limits), now read in Auth0's real {log_id, data} webhook framing, which the receiver had rejected. User created, updated and deleted events arrive through a separate audit Event Stream, and the Auth0 proxy reports its own sign-in, grant and token work. As a demo, selected access and ID tokens and the first 2 KiB of request headers are kept for 30 days. Auditors can reveal them, and every reveal is recorded; refresh tokens are never stored. Capture started with the 20 October deploy and does not backfill. Deployed on 20 October; tenant logs, lifecycle events and the Auth0 proxy's own events now arrive.
Account
Finish sign-up after verifying your email
After you verify your email, "Check verification" on the "check your email" page now continues the same sign-in for every application, including agents, because Auth0 re-reads your current profile instead of trusting the stale copy from when sign-in began. If the email is still unverified, the page stays open with a short notice. The shop's /auth/initiate-login address is also its OpenID Connect login-initiation endpoint: it accepts only Auth0's own issuer, optionally forwards a short login hint and a same-site return page, and Auth0's fallback to it now starts a fresh sign-in. Verified locally; not yet deployed, so the Auth0 Terraform changes and the shop must be applied together.
Behind the scenes
Run everything on tarnandtimber.com
The storefront moves to https://tarnandtimber.com, sign-in to https://login.tarnandtimber.com on the new Auth0 tenant tarnandtimber-prod.us.auth0.com, and the sign-in screen assets to https://assets.tarnandtimber.com with a new release (20261020-tarnandtimber). Every Alchemy stack and fixed resource name now starts with TarnAndTimber: TarnAndTimberShop, TarnAndTimberLoginAssets, TarnAndTimberTerraformState (bucket tarnandtimber-terraform-state) and TarnAndTimberCompany. The Auth0 proxy, claim namespace and passkeys use tarnandtimber.com with no legacy sandrino.run identities, and Auth0 email templates link to the new storefront. The old Fernhollow deployment and its Auth0 resources were removed first, so accounts and shop data start empty.
Name components by what they do
Local naming, Terraform and Auth0 application changes; not deployed or applied.
Behind the scenes
Name agent and audit forwarding by what it does
The webshop and MCP server now forward an agent's call and its original proof to the Shop API under the new API scope `agent:forward`, through the Auth0 applications "Tarn & Timber Webshop Agent Forwarder" and "Tarn & Timber MCP Agent Forwarder". Their settings are now WEB_AGENT_FORWARDER_CLIENT_ID and _SECRET and MCP_AGENT_FORWARDER_CLIENT_ID and _SECRET. The Auth0 proxy forwards genuine audit events through AUTH0_AUDIT_FORWARD_URL and AUTH0_AUDIT_FORWARD_TOKEN to the new webhook path /webhooks/audit/auth0/forward, with delivery mode "forward" and a "forwardedBy" field. No old names are kept. Every Auth0 application in Terraform now also carries a plain description of which component uses it and why. Verified locally; nothing is deployed, so the Terraform change, the scope and the new settings must be applied together before they take effect.
Behind the scenes
The Worker in front of Auth0 is now called Proxy Auth0
The Worker that sits in front of Auth0 and implements the personal-agent capabilities is now called Proxy Auth0: the app lives in `apps/proxy-auth0`, its package is `@retail-demo/proxy-auth0`, and its Alchemy stack is `TarnAndTimberProxyAuth0` with the root commands `cf:dev|plan|deploy|destroy:proxy-auth0`. Its settings now use the `PROXY_AUTH0_*` prefix (for example `PROXY_AUTH0_ISSUER`, `PROXY_AUTH0_SERVICE_SECRET`, `PROXY_AUTH0_SIGNING_KEYS`), and Auth0's custom-domain key is now `AUTH0_CUSTOM_DOMAIN_PROXY_KEY`. In the root composition the Auth0 proxy's own Auth0 settings drop the doubled prefix: `PROXY_AUTH0_DOMAIN`, `PROXY_AUTH0_TENANT_DOMAIN`, `PROXY_AUTH0_AUDIENCE`, `PROXY_AUTH0_CLIENT_ID` and `_SECRET`, `PROXY_AUTH0_MEDIATED_REALM`, `PROXY_AUTH0_CONNECTION_ID`, `PROXY_AUTH0_MANAGEMENT_CLIENT_ID` and `_SECRET`, `PROXY_AUTH0_UPSTREAM_ORIGIN` and `PROXY_AUTH0_CUSTOM_DOMAIN_KEY`; `PROXY_AUTH0_CLIENT_ID` is the same Auth0 application the Shop API and MCP server recognize. The Custom Token Exchange subject type is `urn:poppy:proxy-auth0:session`, audit actions and token purposes use `proxy-auth0.*`, and log events use `proxy_auth0_*`. The shared-secret header is `X-Proxy-Auth0-Secret`, the audit webhook path is `/webhooks/audit/proxy-auth0/streams`, the audit producer is `proxy-auth0`, and the Auth0 applications are "Tarn & Timber Proxy Auth0" and "Tarn & Timber Proxy Auth0 Management" with Terraform addresses, outputs and tfvars keys renamed to `proxy_auth0`. No old names are kept. Existing local `.env` files need the renamed keys. Verified locally; nothing is deployed or applied. The stack, Terraform and settings must change together, and because the stack and Worker are renamed, the next deploy creates a new Worker whose Durable Objects start empty; Terraform likewise replaces the two Auth0 applications, which get new client IDs and secrets.
Keep operator interactions reliable
Local operator interface corrections; not deployed.
Behind the scenes
Keep shortcuts current and avoid repeated setup
The operator command palette keeps one keyboard listener while using the latest committed state. Independent server modules load together, and shared number formatting avoids repeated setup. Sign-in navigation, authorization and audited data access keep their existing ordering and safeguards. Verified locally; not deployed.
Keep investigations faithful to observed evidence
Local review corrections; diagnostic capture remains optional. Not deployed.
Behind the scenes
Bound cleanup recovery and quiet idle investigations
Hourly diagnostic retention drains up to ten bounded chunks independently of financial recovery, and API and operator cleanup runs expose their actual scheduled execution. Storefront and agent API calls send explicit trace context without turning public claims into authority. Runtime filters share the canonical Cloudflare Workflow name; synthetic gateway deliveries create one API receiver observation. Retained header and token tabs no longer generate repeated audited health reads while idle, while explicit releases and cache-clearing safeguards remain. Existing capture and storage ceilings are unchanged. Verified locally; not deployed.
Behind the scenes
Preserve verified context and committed interactions
Actor coverage survives domain and recovery boundaries through one identity-owned adapter. Independent gateway refreshes keep separate actions, authenticated lifecycle effects retain their exact provider parent, and genuine Auth0 deliveries receive one separate API observation. Trusted linked timelines work from either endpoint and include genuine provider records proven by any resolved action; signature-verified callback actors and bounded ingress metadata are visible. Backoffice tables keep committed callbacks and page snapshots interactive while a replacement render is suspended. Capture, retention and release limits remain unchanged. Verified locally; no live provider activation or deployment is included.
Keep diagnostic investigations in context
Local operator improvements; optional capture requires configuration. Not deployed.
Behind the scenes
Search and share diagnostic investigations precisely
Operators can combine exact source, execution, network and up to eight namespaced reference filters, with canonical shareable URLs and preserved time bounds. Selected events keep the investigation and cursor history; closing the responsive six-tab sheet returns opener focus. Actor-chain structural completeness remains distinct from identity verification, while timelines show bounded pages, explicit missing evidence and loading states. Protected token and header values remain temporary and independently gated. No deployment or live provider activation is included.
Search diagnostic evidence precisely
Local implementation; diagnostic ingestion remains opt-in. Not deployed.
Behind the scenes
More precise diagnostic searches
Auditors can combine producer, simulation, runtime and action filters, search either observed IP address, and find all recorded actors. Invalid actor chains are identified explicitly. Measured storage allowances reduce unnecessary short-event drops while retaining overflow allowances and existing ceilings. Local gateway delivery settings remain optional. Recorded evidence stays consistent while concurrent work finishes. Legacy actor lookup reports unavailable when its prior reservation cannot safely fund the index.
Explain committed cart changes
Local implementation; diagnostic ingestion remains opt-in. Not deployed.
Behind the scenes
Separate cart requests from actual committed changes
Cart diagnostics show who added, changed, removed, cleared or merged a cart, with actual quantity changes, no-ops and refusals. Concurrent capped adds use atomic results; a response-read failure cannot hide a committed change. Guest cart merge remains a separate action related to sign-in, while verified Storefront/API transport evidence connects a cart command across services. Guest capabilities and cookies stay excluded. Diagnostic outages do not block successful cart changes; no deployment is included.
Follow tool and conversation execution
Local implementation; optional diagnostic capture remains default-off and is not deployed.
Behind the scenes
Trace agent commands without retaining conversations
Operator diagnostics distinguish tool admission, capability returns, output failures and cancellation requests, with known agent and represented-user context. Separate commands and polls retain separate actions; matching message retries and supported ConversationHub work continue their original context after restart. Prompt, transcript, tool argument and result bodies stay excluded. Existing business authorization, payment, retry and cancellation behavior remains unchanged.
Explain account and security changes
Local implementation; not deployed.
Behind the scenes
Trace account changes and incomplete cleanup
Operators can distinguish committed account, address, card and agent-permission changes from pending verification, rejected commands, no-ops and incomplete session or deletion cleanup. Existing protected operations retain their diagnostic action through retries, with safe targets and field names instead of personal values. Account rules, local denial and operator security-history retention remain unchanged. Capture defaults off; this local work does not verify remote cleanup or deploy the feature.
Identify gateway execution
Local implementation; existing optional synthetic stream configuration is required. Not deployed.
Behind the scenes
Follow gateway object invocations without mixing concurrent actions
Gateway diagnostics identify the actual object class, instance, RPC or alarm and fresh invocation, alongside separate lifecycle outcomes. Concurrent calls retain their own action and actor references; RPC and alarms do not inherit HTTP headers or network identity. Native callback scopes also preserve validated device and sign-in continuations when diagnostics are enabled. Delivery remains bounded and optional, without changing replay, rate-limit, signing or authentication decisions. Requires the existing synthetic stream setup; no deployment is included.
Inspect gateway authentication outcomes
Local implementation; synthetic stream setup remains opt-in and is not deployed.
Behind the scenes
Distinguish gateway authentication from genuine provider events
Diagnostics can receive separately authenticated gateway sign-in, device, step-up, session, grant, rotation and revocation observations, labelled as simulations alongside genuine Auth0 streams. Actual provider attempts and Durable Object lifecycle work retain their own execution and owned action references. Bounded independent delivery reports incomplete coverage without blocking authentication or revocation. Source network facts and eligible demo access/ID attachments follow the existing protected inspection rules; machine/cache observations stay metadata-only. Requires dedicated first-party stream configuration, defaults off and must never capture production credentials. No tenant activation or deployment is included.
Follow sign-in and security verification
Local implementation; not deployed.
Behind the scenes
Trace browser and operator authentication outcomes
Diagnostics preserve one protected action through sign-in and validated callbacks, distinguish provider success from application admission, and show refresh, logout, revoked sessions and security-proof outcomes. Eligible response tokens use the existing bounded demo-only inspector; missing capture remains an explicit gap. Authentication rules and the mandatory operator ledger remain unchanged. Capture defaults off; optional tenant correlation is not verified or activated by this local work.
Follow order changes and refund recovery
Local implementation; optional diagnostic capture remains default-off and is not deployed.
Behind the scenes
Distinguish refund attempts from completed order changes
Operator diagnostics distinguish cancellation and return requests, refund outcomes, lease contention and actual committed order changes. Separate follow-up actions link back to checkout, while existing financial recovery resumes the winning action after restart or missing diagnostics. Exchanges without monetary refunds and repeated no-ops stay explicit. Existing payment keys, recovery policy and public order responses remain unchanged.
Follow checkout through payment and commitment
Local implementation; diagnostic capture remains default-off and is not deployed.
Behind the scenes
Keep checkout diagnostics together across payment callbacks
Optional operator diagnostics distinguish validation, reservations, bank challenges, pending or failed payments and actual paid-order commitment for browser and saved-card checkout. Signed payment callbacks and existing recovery resume the initiating action after restart, even when diagnostics were unavailable. Order and payment filters reuse bounded timelines; legacy or expired history remains incomplete. Payment rules and customer order responses remain unchanged.
Follow internal shop execution
Local implementation; not deployed.
Behind the scenes
Trace Durable Object and scheduled shop work
Diagnostics identify API-owned AccountWriter and ConversationHub calls, alarms and scheduled job steps with separate execution IDs. Verified parent facts stay isolated across concurrent calls, while pending conversation work retains only minimal causal references across restart. Existing operator filters and timelines expose class, instance, invocation and workflow run. Capture remains default-off and demo credential retention must never use production credentials; no deployment is included.
Observe real identity-provider requests
Local implementation; optional provider correlation requires tenant verification.
Behind the scenes
Distinguish identity-provider attempts from cached results
Application diagnostics now show actual Auth0 HTTP attempts, supported callback and rotating refresh traffic, safe endpoint/timing/outcome details and separate cached or shared customer-token reuse. Existing SDK caches and session protection remain intact. Company operator sign-in is labelled with its actual provider. Optional verified tenant features propagate owned action IDs through Universal Login preparation and Management writes; absent mappings stay explicit. Gateway transport has an independent observer seam for later synthetic stream wiring. Capture remains default-off; no tenant activation or deployment is claimed.
Inspect observed demo tokens
Local implementation; capture stays off by default and no deployment is included.
Behind the scenes
Inspect token changes at actual service hops
Auditors can inspect bounded access and ID tokens from owned SDK responses beside actual verification evidence, including MCP exchange and API use. Incoming tokens remain limited to retained cookie-free headers. Protected releases require both audit gates; the sheet decodes locally, explains unavailable or unsafe representations, and requires explicit raw reveal or copy. Cache reuse preserves separate actions and loaded credentials clear with the sheet, session, policy or retention deadline. Capture defaults off; retained live credentials may be reused and must be isolated demo credentials.
Follow application service journeys
Local implementation; not deployed.
Behind the scenes
Connect application observations with verified transport evidence
Separate private producer bindings capture Storefront, MCP and operator requests, including denials, with bounded cookie-free headers. Owned action context and actual API response IDs connect matching service attempts while public claims stay unverified. Actor chain and separately paged Events/Flow views expose verified service hops, missing evidence and exact action navigation. All supported OAuth actors and typed sessions are searchable. New operator ledger entries project forward without changing their local access gate. Bounded capture reports known local overflow as lower-bound health; end-to-end completeness remains explicit. Capture stays default-off; no deployment or historical backfill is included.
Investigate genuine Auth0 observations
Local implementation; tenant stream activation and entitlement checks remain required.
Behind the scenes
Distinguish genuine provider activity and safe relay delivery
Operators can inspect normalized Auth0 tenant logs and user lifecycle events with direct or gateway-relayed provenance and stream health. Accepted audit deliveries persist before acknowledgement, retries keep immutable identities, and action echoes join only matching owned tenant/client evidence. Optional gateway audit forwarding cannot prevent account revocation. Raw provider payloads and transaction state are excluded; production outbound correlation coverage and tenant activation are separate work.
Inspect bounded demo request headers
Local implementation; not deployed.
Behind the scenes
Inspect bounded request headers with audited access
Auditors can explicitly inspect a runtime-visible request-header snapshot in the event sheet. Cookies and known session relays are excluded before capture; intact entries share a 2 KiB serialized budget. Capture and reads require the default-off demo credential flag and both audit release gates. Retained credentials are unredacted and reusable: never enable this in production; use isolated demo credentials. Raw values stay in sheet memory and clear when the sheet or session ends. Token attachments remain unavailable.
Follow Shop API requests
Local implementation; not deployed.
Behind the scenes
Investigate one Shop API action
Auditors can follow incoming requests and their HTTP results in a filtered diagnostic table and right-hand detail sheet. Capture needs explicit deployment scope and database-capacity settings; bounded best-effort storage and thirty-day expiry preserve successful shopping operations when diagnostics fail. This initial local slice covers API requests, with raw headers, tokens and additional producers still unavailable.
A consistent operator dashboard
Local implementation; not deployed.
Behind the scenes
Complete the operator controls migration
The operator dashboard now uses one Base UI foundation for navigation, menus, customer tabs, filters and audit details. Its keyboard command palette still finds orders, customers and sections, while mobile navigation closes after choosing a page. Build checks prevent Radix or cmdk from returning to this dashboard; other applications retain their existing controls.
Thirty-day activity history
Local implementation; requires deploying the retention migration and scheduled Worker configuration.
Account
Activity expires after 30 days
Customer activity and operator access history now disappear after 30 days, including old order timelines and saved pages. Hourly cleanup removes expired entries even when financial recovery fails. Order and payment records keep their existing retention; ordinary database backups remain subject to D1 backup retention.
Keep operator investigations in context
Local implementation; not deployed.
Behind the scenes
Inspect operator records beside the audit log
Auditors can open operator event details in a keyboard-accessible side sheet while keeping their filters and page. The audit page uses an isolated Base UI foundation with server filtering, keyset navigation and small-screen controls. The remaining dashboard migration is separate.
A consistent Tarn & Timber identity
Local work, not deployed. Domain and deployed-resource changes remain separate.
Shop
Bring Tarn & Timber across the shop
The storefront, account pages, sign-in screens, emails, backoffice and agent-facing descriptions now use Tarn & Timber. Fern keeps its name. The outdoor design remains familiar, navigation fits narrow agent browsers, and the About and payment information explain the reference shop and its payment modes more clearly.
Account
Keep email links tied to the intended shop
Email previews use the planned tarnandtimber.com address. Generated Auth0 templates keep the existing deployment origin until a different origin is explicitly chosen at build time, while application emails retain their runtime origin. This covers shop links and images; provider-issued verification and recovery links remain unchanged. The new domain has not been activated by this work.
Behind the scenes
Give the reference shop consistent package names
All apps and shared packages now use the @retail-demo namespace, with matching development commands and package checks. Deployment code uses the new package references while existing infrastructure names and state stay intact, preserving the ability to remove the current deployment later. No infrastructure or domain changes have been deployed.
Accurate API authentication requirements
Local work, not deployed.
Behind the scenes
Document authentication for guest API calls
The generated API specification now requires a bearer token for guest-capable operations, matching the API’s existing authentication checks. Guest visitors continue to browse and use their carts through the storefront, which supplies its own API credentials.
Simpler maintenance tooling
Local work, not deployed.
Behind the scenes
Run database checks with the normal test suite
Database tests now compare the committed SQL migrations with the current schema without requiring old Git commits or archived snapshots. The separate verification command and export are removed. The active migrations and checks that upgrades preserve existing data remain in place.
Behind the scenes
Check live agent integrations in one place
Manual MCP checks now verify discovery and token-exchange permissions, failing on excess scopes or provider errors. Poppy checks validate issued token claims and call the current cart tool. These checks replace the retired research scripts. Offline fixtures run with the standard checks; live MCP acceptance requires the exchange client credentials and browser consent.
Behind the scenes
Use the recovery endpoint for operator retries
The standalone account recovery command has been removed. Operators can resume an existing approved email change or deletion through the documented recovery endpoint with its dedicated secret. Customer retries remain available in the storefront.
Clearer account displays and more reliable forwarding
Local work, not deployed.
Behind the scenes
Validate protected permission settings correctly
Deployment checks now read the actual Management API scopes from protected settings. Valid permissions are accepted, while missing session permissions still stop deployment.
Account
Keep recovery codes tied to the current operation
A recovery code and its acknowledgement are cleared before a different security operation is displayed. Late responses, hidden tabs and page exits still cannot restore a retired code. The session inspector also displays structured subject and issuer claims as JSON instead of an unhelpful object label.
Behind the scenes
Preserve the service binding when forwarding requests
Internal API forwarding calls the Worker binding through its owning object, avoiding invalid invocation errors from detached methods. Public-origin forwarding keeps its existing request and response filtering.
Agents
Reject malformed provider agent identifiers
Agent identifiers returned by the provider or supplied in attribution claims must be text with the expected prefix. Arrays and objects are rejected instead of being converted to text during validation.
One MCP endpoint and one scheduler owner
Local work, not deployed.
Agents
Connect through the canonical MCP endpoint
MCP uses one public origin and one resource-linked exchange client for standard OAuth and Poppy callers. The shop /mcp alias, its metadata endpoint and shared proxy credential are removed. Clients connect to the canonical MCP origin. Deployment uses MCP_CLIENT_ID and MCP_CLIENT_SECRET; local development obtains the same single identity from its synthetic issuer.
Behind the scenes
Run scheduled jobs in the API
The API is the fixed owner of hourly financial recovery and housekeeping. The obsolete shop/API owner setting is removed. Renewable job leases still prevent overlapping execution and stop work after a lease is lost.
Simpler account changes and delivery
Local work, not deployed.
Account
Retry pending account changes explicitly
Account deletion now attempts its full cleanup immediately. Pending deletion and login-email changes can be retried from the signed-out status page using the same expiring receipt; approved contact-phone changes and removals have a retry on the profile page. Existing account denial and provider checkpoints remain in force. A separately configured operator recovery command can resume the exact approved email/deletion operation when the customer cannot sign in, including after identity deletion or expiry of the browser receipt. Operators can resend the exact pending verification email with the existing delivery limits. Recovery requires a dedicated API secret; no new action is authorized by a public operation ID.
Agents
Deliver invitations and account notices directly
Invitations attempt delivery directly; a failed or interrupted attempt can be resent by the owner with a fresh link. Account notices use a best-effort send without a stored queue or delivery deduplication, so callback retries can duplicate notices and failures can miss them. Pending disconnected-agent provider cleanup stays visible and can be retried by the account holder. The database migration removes the old notification outbox and its queued notices.
Behind the scenes
Keep automatic recovery for payments
Payment, refund and reconciliation recovery continues automatically, including retained payment cleanup after account deletion. Hourly jobs also expire invitations and remove old conversation and replay-proof records. Non-payment account operations and invitation/notice delivery no longer run background recovery loops; pending work needs the visible retry or operator command.
Browser and Poppy sessions together
Local work, not deployed.
Account
Manage browser sign-ins and Poppy sessions together
Sessions now lists Auth0 browser sign-ins and active Poppy agent sessions, with activity, expiry, permissions and available approval links. Missing approval browsers do not hide active agents; provider outages are shown, and continuation links reach later sessions through bounded historical batches. A fresh identity check lets you end one Poppy session or disconnect its exact access grant and all sessions using that grant. Ending a session leaves its Account Token usable for a new session. Ending a browser sign-in, signing out all storefront browsers, and validated Auth0 logout notifications also deny linked Poppy grants before provider cleanup; independent grants remain connected. Auth0 cleanup can continue asynchronously, browser cutoffs follow storage propagation, and already-authorized work may finish. Requires the existing session-management permissions and gateway service connection; legacy sid-only logout associations remain retryable until verified retained grant records or a subject-bearing notification recover ownership. Incomplete selected-browser cleanup keeps its warning and retry operation through sign-out and sign-in; expired identity checks can be restarted for the same target.
Clearer account actions and a smaller offline assistant
Local work, not deployed.
Account
Account actions reject incomplete or mixed requests
Invitation and profile actions now reject missing required fields or fields belonging to another action with an input error before running the change. Valid requests keep their existing defaults, ownership checks and identity verification.
Account
Verification and saved-card setup recover cleanly
Closing or cancelling invitation verification now releases its popup and ignores late replies from the cancelled attempt. Blocked or prematurely closed windows remain retryable. Card setup resets after a successful save and recovers from unexpected errors. If saving fails after Stripe confirms the setup, Retry saving card repeats only the save with the same confirmed setup.
Agents
Fern uses explicit read commands when its model is unavailable
Without the model configuration, or if the model fails before producing any output or attempting a tool, Fern accepts search <query>, cart, orders, order <number or ID> and help. It no longer guesses products, variants or quantities, or changes a cart in this mode. Other messages show the supported commands and direct you to the shop for changes; human handoff is unavailable. A partially executed model turn is never replayed through the fallback.
Browser agents get clearer argument errors
Local work, not deployed.
Agents
Browser tools check arguments and results
Local work, not deployed: browser agents now receive an actionable error before a tool calls the shop when arguments do not match its advertised contract. Search keywords must be text; numeric page-size strings remain accepted, and default page sizes and cart quantities still apply. Search feedback uses the cleaned keywords. Unexpected shop responses produce a generic server error without exposing response details. Checkout keeps human confirmation, bank challenges and payment settlement, and tools never automatically retry a purchase or cart change.
The storefront runs its own capabilities
Local work toward the storefront running its own capabilities, not deployed.
Behind the scenes
Agents must use new paths for the checkout preview, address and saved cards
Local work, not deployed: the three checkout reads no longer sit under a storefront prefix. The checkout preview is now POST /v1/checkout-previews, the checkout address is GET /v1/me/checkout-address and the saved cards for checkout are GET /v1/me/checkout-methods, and all three are listed in the Poppy document (/poppy/openapi.json) under those paths. The old storefront-prefixed URLs are gone and are not redirected, so an agent or integration that called them must switch. The live identity read GET /v1/me/identity keeps its path and is now named getIdentity in the OpenAPI documents (it was getWebIdentity). Behaviour, scopes and responses are unchanged. The storefront-only response shapes that remained in the Shop API contract were renamed and the unused caller lists were removed; no other endpoint changed.
Behind the scenes
The storefront manages your activity, connected agents and invitations itself
Local work, not deployed: your private activity (reading it, erasing one entry or all of it), your connected agents (listing, renaming and disconnecting them), agent invitations (preparing, creating, resending and cancelling them) and the invited agent's side of an invitation (opening the emailed link and reading the invitation) now run in the storefront itself, and the Shop API no longer has GET and DELETE /v1/me/activity, DELETE /v1/me/activity/{id}, the /v1/me/connected-agents routes, the /v1/me/agent-invitations routes, POST /v1/me/agent-invitation-targets or the /v1/guest/invitation-acceptances routes. The unused endpoint that disconnected every agent at once is deleted. Behaviour is unchanged: erasing an entry that is not yours reports that there is no such entry and changes nothing, disconnecting a personal agent ends its access at once and a Poppy agent's grant is revoked at the gateway before the disconnection is recorded in your activity, invitations keep the limit of ten an hour and the one-minute wait before a resend, a resent invitation replaces the old link, and an expired, cancelled or foreign invitation link cannot be accepted. An invitation link or cookie with over-long values is now refused with an error page instead of being cut short. The storefront now sends the invitation email itself; if the email fails the invitation stays saved and the shop retries it.
Behind the scenes
The storefront records an invited agent’s sign-in itself
Local work, not deployed: when an agent you invited signs in to your account, the storefront now checks the sign-in and records the agent’s session itself instead of asking the Shop API, and signing out ends that session the same way. The checks are the same (the right account and session, this storefront, a sign-in from the last ten minutes, a well-formed approval), and an agent whose approval has ended is still refused. The storefront-only endpoints POST /v1/web/delegated-sign-ins and DELETE /v1/web/delegated-sign-ins/current, and the invited-callback caller they needed, are gone from the Shop API. An invited agent’s sign-in also no longer sends its session cookie twice in one response.
Behind the scenes
The storefront runs account security itself
Local work, not deployed: identity checks before sensitive changes, your sign-in methods and their overview, the in-page code check for inviting an agent, phone and email changes, saved contact numbers and account deletion now run in the storefront itself instead of going through the Shop API, and their storefront-only endpoints (the /v1/me/fresh-intents, security-methods, security-topology, contact-changes, contact-methods, deletion and embedded-verification routes) are gone. Every sensitive change still needs the same recent identity check, bound to the account as it was when you verified. Changing your email still pauses your connected agents until you sign in with the new address, and starting a deletion still stops every agent at once and finishes after temporary failures. Starting a deletion now also blocks the sign-in, tells Poppy and ends every open agent conversation before the page answers, instead of at the next hourly recovery pass (that pass still retries if Poppy is unreachable). The hourly limit on phone verification codes from one address now counts the real address of each visitor, with an IPv6 network (/64) counted as one visitor; before, every signed-in visitor shared one count. Invited agents and Poppy browsers still cannot use any of it. Conversation and sign-in provider failures keep their error codes in the operation log, and you still see only a generic message.
Behind the scenes
The storefront runs guest shopping itself
Local work, not deployed: starting a guest cart, guest checkout, opening a guest order from its private link and looking an order up by number and email now run in the storefront itself, and the Shop API no longer has POST /v1/guest/sessions, POST /v1/guest/checkouts, GET /v1/guest/orders/{id} or POST /v1/guest/order-lookups. The limits are unchanged in size: twenty new guest carts a minute, ten checkout attempts per cart and thirty per visitor address in ten minutes, and the order lookup limits per address and per email, each address counted as the visitor's IPv4 address or IPv6 /64. The guest cart and checkout counters now live under the storefront's own keys in the shared table, so they start from zero rather than continuing the Shop API's counts. A checkout attempt without a cart counts against the address limit again before it is refused as empty, and an attempt turned away by the address limit also counts against its cart, as before. The storefront now checks the signature of the guest cookie before it names a cart, so a forged or expired cookie reads nothing, is dropped, and writes nothing. Guest cart changes still go through the Shop API, which accepts the carts the storefront created. The order page and the lookup show the same results and errors as before.
Behind the scenes
Agents and API callers can no longer rely on Idempotency-Key
Local work, not deployed: the shop no longer remembers an order request to answer a repeat of it. The Idempotency-Key header (and the idempotency_key argument of the MCP tools) is ignored, and the Idempotent-Replayed header and the idempotency_key_conflict and idempotency_request_in_progress errors are gone. Every order request places an order: the storefront form disables while it submits, and sending it again after a paid order is refused because the cart is empty, but an agent that retries a saved-card or guest checkout after a timeout, a second tab, or a retry while a bank challenge is pending can place a second order. Check the orders before retrying. The idempotency table is dropped and its stored answers are gone.
Behind the scenes
The storefront shows stock, merges your guest cart and shows receipt progress itself
Local work, not deployed: live stock on product pages, merging your guest cart into your account cart when you sign in, and the progress and resend buttons on the account status page now run in the storefront itself instead of going through the Shop API, and the storefront-only endpoints (GET /v1/catalog/stock, POST /v1/me/cart/merges, GET /v1/receipts/progress and POST /v1/receipts/deliveries) are gone. Stock and the pages look the same. A guest cart link that was forged, expired or signed with another key merges nothing, and a guest cart merges once. Sending another verification email keeps its cooldown and the limits per account and per network address, and a deletion receipt can show progress but never resend.
Behind the scenes
The storefront shows your profile and finishes sign-in follow-ups itself
Local work, not deployed: the profile page, the email-verification step after you sign in, and the claim of guest orders placed with your verified email now run in the storefront itself instead of going through the Shop API, and their storefront-only endpoints (GET /v1/web/me/profile-page, PUT /v1/me/profile/email-verification and POST /v1/me/guest-order-claims) are gone. The profile page shows the same details and verification methods as before, with or without a Management client (without one it asks Auth0 for your own profile with your own sign-in and checks it is really you). Signing in after verifying a new email still finishes the email change and gives your connected agents their access back through the gateway; if the gateway has not answered yet that sign-in fails, the shop’s recovery job finishes the gateway step, and the next sign-in records the new email. Guest orders are claimed once and only for the email Auth0 reports as verified. Invited agents and Poppy browsers get no profile page and claim nothing.
Behind the scenes
The storefront places orders and finishes bank checks itself
Local work, not deployed: placing an order in the browser, and settling it after a bank challenge (3-D Secure), now run in the storefront itself instead of going through the Shop API, and the storefront-only copies (POST /v1/checkouts/on-session and POST /v1/checkouts/{id}/completion) are gone. Every submission checks that the checkout permission is held, the account is active and an invited agent's access is unchanged. The page the bank sends you back to only finishes a payment that Stripe itself reports as successful, for your own order (or the guest order your private link opens); anyone else's order answers not found, and repeating the visit changes nothing. Running out of stock during payment is reported as before, and the checkout tools offered to an agent in the browser follow the same path as the page.
Behind the scenes
The storefront saves address changes itself
Local work, not deployed: editing an address and choosing the default address now run in the storefront itself, through the same per-account writer the Shop API uses, so a change made on the storefront and one made by an agent at the same moment are applied one after the other and neither is lost. Adding an address uses the public endpoint agents use (POST /v1/me/addresses), and the storefront-only copies (/v1/web/me/addresses and /v1/web/me/default-address) are gone. The address form, its messages and checkout behave as before, an account that is being deleted still refuses address changes, and an invited agent with the account-write permission can still change addresses.
Behind the scenes
The storefront reads the cart, orders and cards from the public API
Local work, not deployed: the cart drawer, the order pages, the account overview, checkout and the payment page now read the cart, the orders and the saved cards from the same public endpoints agents use (/v1/cart, /v1/me/orders and /v1/me/payment-methods), and the storefront-only copies of those three reads (/v1/web/cart, /v1/web/me/orders and /v1/web/me/payment-methods) are gone. The pages look and behave as before, including which agent added a cart line or placed an order. The public endpoints gained what the pages need: a cart line says which agent added it, an order says whether a cancellation is pending and which platform an agent runs on, the order list takes a cursor and returns the cursor of the next page, a saved card says whether it is held by Stripe or simulated, and a card list read by the person also carries the billing details and the number of pending card changes. Agents never receive billing details or pending card changes, and an agent using the storefront with only the checkout permission still sees the checkout methods but not the card list.
Behind the scenes
The storefront runs card controls itself
Local work, not deployed: adding a test card, editing a card's expiry and billing details, removing a card and choosing the default card now run inside the storefront against the shop data, and the four storefront-only API endpoints for them (/v1/web/me/payment-methods and /v1/web/me/default-payment-method) are gone. The payment page looks and behaves as before. Only the account holder can use these controls: an invited agent or an agent's browser is refused with "Only the account holder can use this operation", another customer's card cannot be changed, and an account that is being deleted refuses card changes. Starting and finishing a Stripe card setup still go through the API.
Behind the scenes
The storefront runs Stripe card setup itself
Local work, not deployed: starting a Stripe card setup and finishing it, both from the payment page and on the page Stripe returns to after a bank check, now run inside the storefront, and the two storefront-only API endpoints for them (/v1/web/me/card-setups and its completion) are gone. The return page reads the setup from Stripe, checks it is yours and that it succeeded, and then saves the card once: following the link again, or Stripe's own notification arriving at the same time, still leaves one saved card, and a declined or someone else's setup saves nothing. Without Stripe configured, starting a setup answers "Card payments aren't set up yet." Only the account holder can add a card: an invited agent or an agent's browser is refused. The payment page looks and behaves as before.
Website quality pass
Local quality pass on the website, not deployed.
Behind the scenes
Every form and button request is checked
Local work, not deployed: each action the website sends to its server, such as saving an address or card, cancelling an order, changing the cart, placing an order or running an assistant tool, now has its own list of allowed fields, and a request that does not match it is refused with a 400 error instead of failing with a 500 server error. The requests that load page data, such as an order, the checkout or the sessions page, are checked the same way. Before, the add-address form accepted any value for where it was opened from, and a malformed account request could crash the handler. Permissions are unchanged: every action still checks who is asking and makes no API call for a visitor who may not do it.
Shop
Photos sized to your screen
Local work, not deployed: every product and editorial photo now comes in smaller WebP sizes (400, 800 and 1400 pixels wide for products; up to 2400 for the scenes; never wider than the original photo), and your browser downloads the one that fits the space and screen instead of the full 1400 or 2400 pixel JPEG. On a 390 pixel phone the photos of the Basecamp 4 product page add up to about 39 KB instead of 1.4 MB, the home page photos about 167 KB instead of 4.6 MB, and on a 1280 pixel desktop 53 KB and 297 KB. The home page photo loads first, at high priority, and every photo reserves its space so the page does not jump. The second photo a product card shows when you hover is no longer downloaded on touch screens.
Shop
Lighter first load on every page
Local work, not deployed: the text of the changelog, about, shipping and privacy pages is now fetched only when you open one of them, instead of with every page. The script every page loads shrank from 559 KB to 461 KB (174 KB to 144 KB compressed), and the scripts needed before a page can start came to 609 KB instead of 798 KB (197 KB instead of 253 KB compressed), because the validation library is no longer part of the first load. These pages still arrive fully rendered, and unknown page addresses still give a not-found page. The checkout and order pages also fetch the cart, order, stock and confirmation data at the same time instead of one after the other, and an order that does not exist still gives the not-found page when the stock read fails at the same moment.
Behind the scenes
Retired website addresses removed
Local work, not deployed: the website no longer redirects server calls from the old /_serverFn address to /account/_serverFn, and no longer keeps /api/stripe/webhook, which only answered 410 Gone; it now gets the same 404 as any unknown /api address. Stripe events go only to the API at /webhooks/stripe, and the setup guide now says so.
Shop
Checkout, order lookup and product page fixes
Local work, not deployed: the checkout page shows a payment notice only for the two outcomes Stripe's return can leave (failed or pending), so a hand-typed address such as ?payment=toString no longer reads an inherited value. The order lookup asks for the number with a numeric keyboard instead of a number field (no more e or scroll-to-change), always has its result area ready so a found order is announced, ties a failed lookup to both fields and moves focus to the first, and its button is an explicit submit button. Product pages no longer print undefined in their canonical and social image links when the page data is missing. The checkout completion route no longer asserts a guest token it has not checked. The sessions page reads the sign-out result and the identity check from its address on the server, so the notice and the proved sign-out button are in the first HTML instead of appearing after the page loads.
Account
Forms point at what needs fixing
Local work, not deployed: after a failed save, the address, card, card-edit, return, profile email and phone, guest checkout and order lookup forms move focus to the first field to fix, and the country list and every error message are tied to their fields for screen readers. The return form now marks the quantities, exchange choices or reason it needs. Address fields at checkout use the shipping autofill group, the card expiry edit uses text fields with the card-expiry autofill hints instead of number fields, and the new and confirmation email fields on the profile page no longer offer your current email. Every button states whether it submits, so none submits a form by accident.
Shop
Cart drawer: errors, focus and announcements
Local work, not deployed: when adding to the cart fails, including a lost connection, the message appears inside the open cart drawer as an alert instead of behind it. Removing a line moves focus to the next line, or to Browse all gear when the cart empties, instead of dropping it on the page behind the drawer. A quantity change is announced to screen readers, the VAT note sits outside the totals list, and the checkout button clears the iPhone home indicator.
Shop
Readability, contrast and touch targets
Local work, not deployed: form field borders are now 3.7:1 against the page (they were 1.5:1) and placeholders use full-strength text, the agent banner's secondary text reaches 4.5:1, and the Outfitters caption under the logo is no longer 9 pixels. Small buttons, the cart steppers and the Fill in test card button are 44 pixels tall on touch screens. When the agent banner is showing, sticky sidebars and order summaries sit below it and scrolled-to or focused elements are no longer hidden behind it. Search pages put their heading first, with product titles as second-level headings, and the collection list is named by its label alone. The focus ring keeps each control's own shape, and tapping gives a visible highlight.
Account
Account page details
Local work, not deployed: the activity list shows dates and times in the shop's locale and Brussels time, like every other date, and keeps its table structure for screen readers on phones. The session debugger's capture time uses the same format. The profile, delete account and email status pages have their own titles, and the profile page is kept out of search results. Refresh progress on the email status page reloads its data in place instead of reloading the whole page, and the invite dialog no longer scrolls the page behind it.
Behind the scenes
Account activity uses a fixed list of event names
Local work, not deployed: the account activity API now returns only event names the website knows how to describe, and reports any other recorded event as other. The activity page looks the same: such events were already listed as Account activity recorded.
Shop
Page addresses are checked
Local work, not deployed: every page that reads its address, such as search, collections, product, order, checkout and the account pages, now checks each value it knows. A value it cannot use, such as an unknown sort order or an empty search, is dropped, and the server answers with a temporary redirect (307) to the same address without it. Parameters a page does not use, such as utm_source campaign tags, are kept, and a repeated parameter still counts by its first value. After a bank's 3-D Secure check, the browser loads the order completion address as a full page, so the server finishes the order and sends you on.
Sign-in screens: clearer errors and better accessibility
Local quality pass on the Fernhollow sign-in and sign-up screens, not deployed: the live release pin is unchanged, so customers see these changes only after the next Universal Login release.
Account
Clearer sign-in errors
Local build, not deployed: a failed sign-in or sign-up shows each message once. Auth0 messages are no longer followed by a generic "could not be completed" line, and a message about one field appears only under that field instead of again in the summary. A field message whose field is not on the screen, such as the account on the password step or a widget CAPTCHA, stays in the summary, and when the only problem is at a field, that field receives focus, even if the Auth0 message arrives just after the page loads. A password that breaks the requirements, or two new passwords that differ, now marks the fields as invalid, moves focus to the first invalid field on every attempt, and the password field points to the requirements list, so screen readers announce the problem where it happens. On the email code screen, requesting another code now says that a new code is on its way, and the countdown no longer redraws the screen while no code is pending. The password reset confirmation no longer pretends to change its button after a resend, because the page reloads.
Account
Better password manager and mobile keyboard support
Local build, not deployed: the password steps of sign-in and sign-up carry the account identifier as a hidden username, so password managers save and fill the password for the right account. Email, username and recovery code fields no longer capitalise or spell-check what you type, and the fields keep a 16 pixel font so iPhones do not zoom in when you tap them. Combined sign-in, which has no passkey support in its Auth0 screen, keeps the plain username hint.
Account
Accessibility fixes on the sign-in screens
Local build, not deployed: field errors are no longer read as part of the field name; the main landmark holds only the form and the brand panel stands beside it, with the screen heading first; the Show button keeps the same text and name, and its pressed state (underlined) tells you whether the password is visible; field outlines and outlined buttons reach 3:1 contrast, and links, the Show button and text actions are at least 44 pixels tall. Disabled buttons show a not-allowed cursor and only a busy form shows a progress cursor. Choosing a verification method, an email address or a factor to add are now plain buttons with a separate Back control instead of a form whose submit was Back, the button you choose keeps keyboard focus while the request is sent, and Back to the shop is a link. On the email code screen, the resend and alternate actions share one wrapping row. The pages declare a light colour scheme and a theme colour so browsers do not darken them; the release builder adds both tags to the head tags of the next release.
Account
CAPTCHA loading fixes and lighter photo loading
Local build, not deployed: Cloudflare Turnstile challenges now render once their script has loaded instead of failing with an error from its ready() call, while reCAPTCHA keeps waiting for its own ready signal. A provider script is requested once per page even if the screen redraws while it loads, and a load error disappears when the provider or site key changes. On desktop, only the first brand photo loads normally; the other three load lazily at low priority.
Behind the scenes
The sign-in development preview works again
Local build: the development server publishes the selected screen context in the page head before any Auth0 module loads, which fixes the "Universal Login Context is not available" failure of the preview. The preview now also runs in React strict mode to surface effect bugs early. Production output is unchanged.
The Shop API moves to oRPC
Local Phase 4 work of the oRPC plan, not deployed: every endpoint of the Shop API is one typed procedure, and the website, the MCP server and the backoffice call it through one generated client.
Behind the scenes
One typed Shop API with new routes and one error format
Local build, not deployed: the Shop API is built from a single contract, and the website, the MCP server and the backoffice call it through a generated client. The MCP server and the backoffice check input before sending anything; the website ships only the routing part of the contract, which keeps its server bundle small, and lets the API check its input. Routes are now named as resources, for example checkouts, cancellation of an order and delivery of a receipt, and the old paths are gone. Every failure is one problem document with a stable error code, the request id and, where it applies, the field issues, and a missing sign-in is now a 403 for everyone, as the protocol says. A request body must be sent as JSON with a content type. Nothing of this is visible on the website itself.
Agents
Agents use the same API as everyone, described at one address
Local build, not deployed: Personal Agents call the ordinary /v1 API with their DPoP-bound token instead of a separate copy, and read its OpenAPI description at /poppy/openapi.json, which lists only what agents may use and names the account scopes each operation needs. The separate agent copy and its hand-written description are removed. Error answers carry the protocol error code and sign-in challenges name the proof algorithm.
Agents
MCP tools have new names
Local build, not deployed: the MCP tools are generated from the same contract as the API, so their names changed from snake_case to camelCase, for example searchProducts, getCart, addCartItem and checkoutSavedCard. Their inputs are unchanged, tools that repeat safely accept an idempotency key, and the shop assistant uses the same names. Clients that remember the old names must refresh their tool list.
Agents
Conversation streams use the standard event format
Local build, not deployed: a conversation can be read as a page or as a stream of server-sent events on the same address, with the event id as the stream id, resuming from the last id and a comment every 15 seconds. A stream ends quietly when the conversation closes, ten minutes pass or the agent loses access, and the agent that reconnects gets the normal sign-in challenge. Partial-text events are not sent, because complete messages are enough on this platform, and message bodies over 64 KiB are refused.
Behind the scenes
Catalog reads can be revalidated, creates say where the new item is
Local build, not deployed: catalog reads carry a validator and answer 304 when nothing changed, which saves clients from downloading the catalog again. Everything else, including account and checkout answers, is marked as not to be stored. Placing an order, signed in or as a guest, answers 201 with the location of the order, and repeating a request with the same idempotency key returns the same answer and location.
Agents follow the Personal Agent Protocol more closely
Local Phase 2 work of the oRPC plan, not deployed: gaps found in the review of the Poppy surfaces are fixed on the current stack, with a spec-derived test suite that guards the coming rewrite.
Account
Account buttons wait until the page is ready
Local build, not deployed: the buttons and fields on account pages stay disabled for the moment between the page appearing and it becoming interactive. Before, typing or clicking in that moment, for example right after confirming your identity, could be lost silently, so a deletion or a contact change seemed to do nothing.
Agents
A narrower token stays narrow
Local build, not deployed: an agent that asks for fewer scopes than its Session has now acts with only those, on the REST API, the MCP server and the shop assistant. A read-only token on a read and write Session can no longer cancel an order, and the refusal names the account scope it lacked. When an agent switches to a narrower token in the middle of a conversation, the assistant is held to it from the next request on. Streams that are already open keep the token they opened with. A signed-out token is treated as signed out even after its Session signs in, on the API and the MCP server, and gets a sign-in request for account actions. On the API a signed-in token whose Session signed out is treated the same way; the MCP server refuses it as an invalid token.
Agents
A retried message gets its original answer
Local build, not deployed: if an agent resends a message it already sent, it now receives the response it first got, including the events of a request that waited for a reply, even if the assistant has answered or the conversation has closed since. This holds across restarts and from another Session of the same person. A request that waits for a reply returns at most 256 KiB of events and says if more follow. Asking for a person now shows the conversation waiting and then going back to the assistant, with the explanation as a message.
Agents
Clearer sign-in challenges and shared replay protection
Local build, not deployed: a missing, malformed or expired token is answered as an invalid token in the scheme of the token that was sent, and a plain Bearer Session Token is refused outside MCP. Conversation requests now count against the same per-Session and per-agent request limits as the API and get a numeric Retry-After. A DPoP proof that one server accepted can no longer be replayed at another, because accepted proof identifiers are shared, and an hourly job deletes the expired ones in batches until none are left.
Agents
MCP accepts standard clients and Poppy agents
Local build, not deployed: the shop MCP server on its own domain accepts two kinds of sign-in. Standard MCP clients, registered by Dynamic Client Registration or by a Client ID Metadata Document, get a plain access token with no Poppy Session and use the shop on your behalf with the shop agent scopes. Poppy agents sign in at the shop sign-in service and get a Session token, whose permissions are limited to what the live Session still grants. The server now names both the shop sign-in service and the Auth0 tenant as places to sign in, and the tenant advertises Client ID Metadata Document support (each such client is registered once by a tenant admin). Tokens bound to a proof key are still refused, and refusals name account scopes for Poppy agents and OAuth scopes for standard clients. The deploy no longer fails when a discovery document lists the MCP server without Poppy settings; the sign-in service issuer is still checked.
Agents
Conversations stay hidden, streams end quietly, retries keep their answer
Local build, not deployed: an agent signed in to a different account than the one a conversation belongs to is told the conversation does not exist, as any other outsider is, instead of being asked to sign in. A conversation stream whose Session ends is closed without an error event, which the protocol does not define, and the agent that reconnects gets the normal sign-in challenge. A retried message returns its original stored answer even if its cursor has been pruned since, and those stored answers are kept as long as their conversation.
The Shop API stops tracking web sessions
Local Phase 1 work of the oRPC plan, not deployed: signed-in requests are checked by their access token alone, and the website keeps its own sessions.
Behind the scenes
Signed-in calls need only your access token
Local build, not deployed: the Shop API no longer records or checks website sign-ins. It verifies the access token, the caller and the scopes, and owns your data by the token subject. The signed session context, sign-in registration and the sign-in tables are gone. The cart merge, the verified-email copy and the fresh-identity check each have their own call at sign-in. Revoking a session now ends the website session, but an access token already issued works until it expires (accepted for now). Sign-in history entries are no longer recorded, and changing your email no longer signs out other browsers.
Account
Sessions page and sign out everywhere
Local build, not deployed: the sessions page shows this device and offers Sign out and Sign out everywhere. After an identity check, Sign out everywhere ends every website session at Auth0 together with its refresh tokens, and agent sign-ins are left alone. If Auth0 cannot be reached, this device is still signed out and the page asks you to try again. The website now receives Auth0 back-channel logout itself and ends only its own session; a notification it could not record is answered as temporary, so a retry from Auth0 still takes effect. The device list and per-device sign-out are removed.
Account security through the Shop API
Local Phase 7 through Phase 10 work, not deployed: the website and operator dashboard use the Shop API for shop data, while My Account verification stays on the website. Live traffic and compatibility windows still gate the contract releases.
Behind the scenes
Shop data and background work have one owner
Local contract build, not deployed: the Shop API owns storage, conversations, Fern, Stripe webhooks, Auth0 sign-out notifications, account verification checks and hourly recovery. The website and operator dashboard use API calls only, and the gateway only issues credentials. Owner-run traffic and compatibility windows still gate release.
Account
Signing in again when your session cannot refresh
Local contract build, not deployed: when the website cannot obtain your Shop API token, it restarts sign-in. Account pages and changes no longer run through a local fallback. My Account verification stays on the website.
Shop
Older guest cart cookies retired
Local contract build, not deployed: guest carts use the signed fh_guest cookie alone. Raw fh_cart cookies and cart wrapping are retired after the owner confirms thirty days since activation and seven days without legacy wraps. Old account-status receipts are retired only after their lifetime and in-flight operation window.
Agents
Agent tools and conversations use the Shop API
Local contract build, not deployed: browser tools, MCP, OpenAPI and conversations use API authority, with no legacy webshop handlers or gateway forwarding. Ended or unavailable agent browsers show an empty read-only guest cart. Existing live conversation and transfer acceptance gates remain required.
Account
Remaining account flows can run through the Shop API
Local work, not deployed: profile and contact reuse, private activity, agent invitations and verification, sign-in identity, verified guest-order claims and session diagnostics have API equivalents behind separate switches. Agent banners use a safe account name. Missing customer API credentials restart sign-in. The webshop keeps OAuth cookie rotation and all Auth0 My Account tokens and calls; direct fallbacks remain for the later contract release.
Behind the scenes
Background recovery jobs have one active owner
Local Phase 10 work, not deployed: hourly recovery and conversation retention share renewable database leases. Both Workers can be present during rollout while one configured owner runs each job. The API cron and data-preserving Durable Object transfer build require an owner-run rehearsal before activation.
Agents
Agent conversations can be served by the shop API
Local work, not deployed: conversations and Fern are available directly on the shop API, preserving message retries and event resume. Open streams recheck session and account access every thirty seconds and close when access ends. The local gateway configuration advertises direct conversation, REST and MCP resources.
Shop
Checkout retries reuse the original request
Local fix, not deployed: retries of one form submission or WebMCP confirmation reuse its checkout request key, so the API can return the first result instead of placing an order twice. A new submission receives a different key.
Behind the scenes
Busy agent sessions keep their own replay budget
Local fixes, not deployed: one agent session can no longer fill every session’s proof replay budget. API responses are checked against their declared schemas and request logs use the shared field mapping. Unused account-security coordination routes have been removed; My Account remains web-only.
Agents
Personal agents can connect straight to the shop API and MCP server
Local Phase 9 expand work, not deployed: direct personal-agent requests keep signed-out session carts and signed-in account carts, with live checks before every write. MCP verifies live sessions and uses delegated or narrow relay credentials. A local issuer and supervised direct test suite exercise the new topology; live token-exchange verification and browser acceptance remain owner-run. Phase 10 prepares direct discovery configuration; the rollout switch remains owner-run.
Behind the scenes
Agent browsers and invited agents can act through the shop API
Local work, not deployed: the agents slice uses dedicated relay credentials and rechecks the original session or invitation approval. Read-only invited agents cannot check out or use human-only account actions. Enable the slice only after deploying and configuring the receiving resources.
Behind the scenes
The backoffice reads orders, activity and customers through the Shop API
Local work, not deployed: the backoffice can use authenticated Shop API reads with staff attribution, while operator sign-in and the audit trail are unchanged. It stays on direct reads until BACKOFFICE_SHOP_DATA=api; direct access remains available for rollback until a later release.
Behind the scenes
The Shop API requires its own account encryption material
Local fix, not deployed: the API requires a separate account key and cannot fall back to the website session secret. The webshop keeps the same purpose-key bytes so existing sealed receipts still open.
Account
Fresh sign-in for each sensitive security change
Local work, not deployed: removing a sign-in method or regenerating a recovery code uses a fresh My Account token bound to that action. Recovery codes now come from My Account and are shown once. Actions spend the intent at issuance, and the shop clears the sealed credential after use. Activation requires short-lived provider tokens; API routing stays off until the account-security and sign-ins slices and Action origin are switched.
Behind the scenes
Account sessions, contact changes and deletion can run through the Shop API
Local work, not deployed: separate sign-ins and account-security switches move callback registration, session controls, contact changes, deletion and logged-out progress to the Shop API. Shared versioned purpose keys keep historical receipts readable during rollback. My Account calls remain entirely in the webshop; release switches and cleanup are owner-run.
A central Shop API
Local work, not yet deployed: the Shop API Worker and flag-guarded catalog, guest and customer slices in the shop.
Agents
Connect an AI assistant to the shop over MCP
Local work, not deployed: Claude and other MCP clients can self-register, sign in and act with your consent and agent scopes. Both the canonical MCP URL and the shop's /mcp alias use your account cart. Per-tool token exchange keeps human card controls, account deletion and backoffice operations unavailable to assistants. Tenant adoption and client verification are owner-run before release.
Shop
Checkout and payments can run in the Shop API
Local work, not deployed: checkout, guest-checkout and cards in SHOP_API_SLICES move signed-in and guest checkout, bank-challenge completion, private guest order links and lookup, and saved-card changes to the Shop API. Agents keep their current checkout path. Guest checkout attempts are now limited to 10 per cart and 30 per network in 10 minutes. Both paths share the same cart and payment workflows for rollback.
Behind the scenes
Stripe events can be received by the Shop API
Local work, not deployed: the Shop API verifies Stripe signatures at /webhooks/stripe and finishes payment or card-setup work before acknowledging an event. STRIPE_WEBHOOK_OWNER=api makes the old shop endpoint forward unchanged bytes during the move, with one processing owner. The API receives only purpose-derived sealing keys, never the website session secret. The owner must switch the Stripe endpoint; removing the shop forwarder is a later release after at least three days live.
Behind the scenes
Signed-in carts, orders, addresses and saved-card lists can be served by the Shop API
Local work, not deployed: the cart, orders, addresses and payment-methods switches in SHOP_API_SLICES serve signed-in shoppers through their own Shop API token and a short-lived context tied to their website sign-in. The existing Shop AccountWriter keeps serializing addresses. Only SDK token unavailability uses the authorized local path; API failures stay visible. Checkout and card writes remain in the shop.
Account
Sign-in can ask for access to the Shop API
Local work, not deployed: with login-audience enabled, sign-in requests the Shop API audience and website scopes. Account-security Actions accept it for verified step-ups, and the website refresh token renews Shop API and opt-in My Account access with zero rotation leeway. Concurrent API token requests share one refresh per sign-in within a Worker isolate; coordination across isolates remains pending.
Shop
Empty guest cart cookies stop requesting sessions on every page
Local fix, not deployed: when the API reports that an old guest cart no longer exists, the shop creates one fresh API session and replaces both guest cookies. Later page views reuse it, avoiding repeated wraps and shared address-bucket session limits.
Behind the scenes
Guest carts can be served by the Shop API
Local work, not deployed: guest carts use the Shop API only when SHOP_API_SLICES includes guest-cart. A second HttpOnly fh_guest cookie then accompanies fh_cart, preserving the same cart for checkout, sign-in merge and rollback. Guest session creation is limited to 20 per minute per shopper address bucket; guest cart changes to 60 per cart and 300 per address bucket per minute. Sign-in and sign-out clear both cookies. The storefront client now also requests cart, checkout, orders and guest scopes, and Shop API problem responses carry field issues and missing scopes so the shop shows the same messages either way. Checkout and agent channels stay in the shop.
Behind the scenes
Product stock can be served by the new Shop API
Local work, not deployed: a new Shop API verifies bearer tokens and live sign-in authority before serving catalog reads. Product stock can use its typed client and cached storefront token when SHOP_API_SLICES=catalog; it stays local with the flag unset. The shop-domain /api/v1 proxy accepts only bearer requests and strips browser cookies and session headers. Auth0 grants and Worker bindings are prepared for an expand release; guest cart writes and checkout stay in the shop.
A public documentation site
Local work, not yet deployed: the docs site for docs.tarnandtimber.com.
Behind the scenes
Architecture documentation for the planned shop API and MCP server
A new documentation site, built with Blume, explains the planned central shop API and MCP server: the system map and its two boundaries, each component, who can call the API and how each caller is recognized, step-by-step request flows for checkout, guests, MCP clients, the backoffice and Poppy agents, the API pipeline, how Auth0 is set up, the shop-domain proxies and the deployment order. Every page is marked as planned, because none of that design is built yet. The build also checks that every diagram renders and that no internal link is broken. It is ready to deploy to docs.tarnandtimber.com with the deployment command, which now includes this step; nothing is deployed yet.
Backoffice sign-in with Okta
Deployed on 14 October: Okta configuration and the backoffice at backoffice.tarnandtimber.com.
Behind the scenes
The team signs in to the backoffice with Okta
Terraform now sets up the company Okta org for the operator backoffice at backoffice.tarnandtimber.com: the sign-in application, a backoffice-only sign-in policy, department groups (Customer Support, Fulfillment, Finance, Engineering, Marketing, Leadership) and the backoffice-viewer and backoffice-auditor groups that grant access. Only those two groups can start a sign-in, and only they are sent to the backoffice, which maps them to its viewer and auditor roles. The backoffice address is set once: the same value registers the sign-in and sign-out addresses with Okta and gives the Worker its domain. Seven test personas, from a support agent to a marketing manager with no access, are documented with their roles; in the test org they sign in with a password alone. The deployment command now runs this step and hands the issuer, client and group settings to the backoffice, so they are no longer copied by hand.
Account fixes found by new tests
Local work, not yet deployed: fixes found while adding tests for every account module.
Account
Correct sign-out progress for a device signed out again
Signing out a device that had earlier been signed out, then signed in again, could show its new sign-out as replaced by a later one while it was still in progress. Signing out one device, other devices or all devices now starts the device’s cleanup afresh, so progress reads as pending until it completes.
Account
Identity checks are not used up by a change that cannot happen
If an account changed between your identity check and a sensitive action (another device signing everyone out, or deletion starting), signing out everywhere could report success without ending any sign-ins, and other actions could use up the check. The identity check is now only used when the account is still as it was when you verified, so the action fails and asks you to verify again instead.
Account
Retrying a finished sign-in method removal succeeds
If the confirmation of a removed sign-in method was lost, for example on a dropped connection, retrying the same removal said to start again even though the method was already gone. A retry of a completed removal now succeeds without contacting Auth0 again.
Account
An invalid contact number no longer blocks the next one
A contact phone number that was not in international format was rejected only after your identity check had been used, which left a stuck pending change that blocked adding any other number until it was cancelled, and a resend could still try to text it. The number is now checked before the identity check is used, and a code is never sent to a number that is not in international format.
Account
Auth0 slowdowns no longer stall account changes
When Auth0 asked the shop to slow down and wait a long time, the request waited the whole time, holding up later address and contact changes for that account. The shop now waits at most five seconds; after a longer request the change fails so you can try again shortly, and background clean-up retries on its next run.
Account
Contact phone shown only when codes can be sent
With contact SMS switched on but a Twilio Verify credential missing, the profile page offered to add a contact phone, and adding one then failed. The option is now shown only when every Twilio Verify credential is configured, using the same check as the code that sends the text.
Account
A clear message when a number change is already in progress
Starting a new contact phone change, removing the contact phone or reusing an Auth0 phone while another change was still in progress failed with an internal database error. It now says another phone number change is in progress and to finish or cancel it first, and the new identity check stays unused.
Account
An email change cancelled when the email is changed elsewhere
If your sign-in email was changed outside the shop while a shop email change was still in progress, the change could never finish and kept retrying every minute, leaving the account waiting. The shop now cancels that change, makes the account usable again and shows on the email status page that the change was cancelled and that you should sign in again with your current email. Sign-ins the change had ended stay ended.
A mailbox for tarnandtimber.com
Deployed on 14 October: the mailbox and Email Routing for tarnandtimber.com.
Behind the scenes
Embedded photos show in the mailbox
Photos carried inside a message (referenced as cid: images) used to show as broken images in the mailbox, even with remote images switched on. The mailbox now reads them from the original message when you open it and shows them in place. They load nothing from the internet, so they appear whether or not remote images are allowed. Remote images still load only on request. Live at mailbox.tarnandtimber.com since 14 October.
Behind the scenes
Receive mail at any @tarnandtimber.com address
A new private inbox at mailbox.tarnandtimber.com receives mail for every address on the domain, so the team can sign up for Google and other services with addresses such as google@tarnandtimber.com. Cloudflare Email Routing hands each message to the mailbox Worker, which keeps it in its own D1 database and the original message in R2. The inbox lists mail per address, searches it, refreshes every 10 seconds and shows the one-time code it finds in sign-up messages with a copy button. Messages can be opened, marked unread, deleted one at a time or in bulk, and downloaded as .eml files with their attachments. Sender HTML runs in a sandboxed frame without scripts, and remote images load only on request. The site asks for a shared password once and then keeps the browser signed in for 7 days with a signed cookie; changing the password signs everybody out, and sign-in attempts are limited to five a minute. The mailbox can only receive mail, not send it.
Universal Login quality checks
Local code quality improvements on 14 October; no new release deployed.
Behind the scenes
Simpler sign-in code and a clean production build
The identifier-first sign-in form is easier to maintain while keeping its current behavior. Login lint and React quality checks are clean, and the production build uses the supported bundler configuration without changing the emitted asset bytes.
Stronger development checks
Local development tooling on 14 October; not deployed.
Behind the scenes
Safer automated sign-in releases
Sign-in deployments now recheck every public asset immediately before updating Auth0, including retries of the same release. Concurrent uploads cannot replace existing files. A fresh checkout can rebuild missing assets, but publication stops if they differ from the committed release. These safeguards are implemented locally; production rollout and hosted CI deployment remain pending.
Behind the scenes
Catch code quality problems earlier
Development checks catch invalid module dependencies and accidentally empty test runs earlier, helping keep future releases reliable.
The same sign-in screens for every app, deployed by one command
Deployed on 14 October with pnpm infra:deploy: the Auth0 tenant and email, the sign-in assets, the gateway and the shop. Terraform state now lives in R2.
Account
Branded sign-in for the playground and other apps
Fixed the Poppy Playground, and every app except the website, getting Auth0’s classic sign-in pages. The branded sign-in, sign-up, password reset, passkey and MFA screens were limited to the website application. They are now managed in the Auth0 Terraform with no client limit, so every application on the tenant gets the same 22 screens, all on the 13 October sign-up email release. Before, those screens had been applied by hand one at a time, so the 19 non-signup screens still ran the previous release. Deployed on 14 October: the playground’s sign-in now loads the branded screens.
Behind the scenes
Cloudflare and Auth0 deploy together from committed configuration
pnpm infra:plan and pnpm infra:deploy now run every Alchemy stack and Terraform root in order: the Terraform state backend, the sign-in assets, the email sending domain, the Auth0 tenant and its email, then the gateway and the shop. Auth0 client credentials pass from Terraform to the gateway and shop directly instead of being copied into env files. Terraform state moves from one laptop to R2, and the tenant configuration that decides which resources exist is committed instead of living in ignored local files, so another checkout can no longer plan deletions by accident. Action secrets are now write-only and stay out of state, the customer and invited-agent connections cannot be deleted by a plan, and post-login Actions keep their order on a fresh tenant. pnpm test now also runs every Terraform root’s tests. First used for a production deploy on 14 October.
Account
Fernhollow colours on the remaining Auth0 screens
The screens Auth0 still draws itself, such as setting up an authenticator app, confirming an email address or approving a device, showed Auth0’s default look with a green button on grey. They now use the Fernhollow colours, the shop’s Manrope font and the Fernhollow logo, managed in Terraform with the rest of the tenant. Deployed on 14 October.
Behind the scenes
Login domain, MFA settings and the email token deploy with the rest
More of the setup that was done by hand is now part of pnpm infra:deploy. Terraform adopts the login-shop.sandrino.run custom domain and keeps it as the tenant default, and the gateway gets the domain’s proxy upstream, its DNS ownership record and every Auth0 ID from Terraform instead of copied values in its config. Terraform also adopts the tenant’s MFA settings, and cannot delete them or the login domain. Alchemy creates no API tokens: the R2 token for Terraform state and the send-only email token are made by hand and kept in the root env file, and a new email token reaches Auth0 on the next deploy without a manual version bump. Sign-in assets are uploaded with Alchemy’s own Cloudflare login. Fixed the deploy script corrupting JSON secrets, such as the gateway’s signing keys, when it passed them to the gateway and shop stacks. The proxy key of the adopted domain stays in the gateway’s env file, because Auth0 returns it only once. The old hand-made login proxy Worker, which the gateway replaced, was deleted on 14 October. Deployed on 14 October. Deploys need an Auth0 CLI login that can manage MFA settings and client grants.
Agent consent that matches the shop
Deployed to Auth0 on 14 October: the consent Forms and the sign-in page template.
Agents
Branded permission choices on agent consent
Fixed the agent consent page still showing Auth0’s default green Yes/No radio cards, plain grey permission names and an Allow button on its own row. Each permission is now a compact row with its name and explanation beside a small No/Yes switch, and the heading and text sizes match the sign-in pages. Auth0’s own stylesheet had been shrinking all text on these pages, including email verification, to 87.5%; they now use the same text size as sign-in. Deny and Allow sit side by side. The styles now follow the markup Auth0 actually hosts. Deployed to all seven consent forms on 14 October. A fresh hosted consent transaction still needs browser validation.
Confirmations you can read anywhere on the page
Deployed on 14 October: the shop, with no database migrations.
Account
Confirmations are light cards above the Debug button
The short confirmation after a change is now a light card at the bottom right, so it reads on the beige page and the dark green footer alike, and it sits above the Debug button instead of covering it. It is built on Sonner, the toast library the account sign-in method screens already use, and those screens keep their own messages, so an invalid verification code is still shown once.
Automated account sign-in releases
Local deployment implementation. Production CI configuration is required before activation.
Behind the scenes
Verified sign-in assets and retained releases
pnpm login:release builds the sign-in screens and pins the release in the repository. The next pnpm infra:deploy uploads its assets to assets-shop.sandrino.run through Alchemy, checks that every file is publicly served with the right bytes, type, caching and cross-origin headers, and only then points the Auth0 screens at it through Terraform. Assets are never overwritten or deleted, so a rollback is restoring the previous pin. This replaces a separate CI pipeline that was never deployed. First used on 14 October: the deploy verified the pinned release publicly before pointing the screens at it.
Consistent service diagnostics
Local implementation on 13 October; not deployed.
Behind the scenes
Correlated gateway logs without private error details
The gateway now uses the same structured logger as the shop. Each request has its own correlation ID, and service diagnostics keep only approved operational fields. Provider error messages, stacks, credentials and request contents are excluded. Sign-in, proxy responses and retry behavior are unchanged. This is local work; the new log format requires a gateway deployment.
Every account change confirms itself
Deployed on 13 October: the shop, with no database migrations.
Account
A short confirmation after each change
Changes in your account now end with a short message at the bottom of the screen that screen readers announce, instead of the page silently changing. It covers deleting or making an address or card the default, saving, editing or removing a card, editing an address (the editor now closes once saved), cancelling an order, requesting a return, signing out sessions, removing a sign-in method, deleting activity, and deleting, renaming or re-sending to agents and invitations. Previously, when the change removed the button you pressed, such as a deleted row or a cancelled order, its confirmation disappeared with it. Errors still appear next to the form. Phone changes on Profile & contact now refresh the details in place with a confirmation, such as We sent another code or Contact phone saved, instead of reloading the whole page. Confirmations also work at checkout, for example after saving a card.
A proper invitation page for agents
Deployed on 13 October: the shop, with no database migrations.
Agents
Invitation page shows the offer and what the agent should do
The page an invited agent opens from its email is now a proper Fernhollow page instead of bare text. It shows the agent name and platform, the invited address, the permissions as badges and the exact expiry time, then step-by-step instructions for the agent: ask the person for permission first, sign up in a way the agent can complete alone, choosing and storing its own password rather than asking the person, and review and accept after signing in. It also says until when the sign-in link works and that the agent can open the email link again for a new one, for example after waiting for the person's answer. It still never shows who invited the agent, an ended or unopened invitation now says so in plain words, and the page is fully readable without JavaScript.
Clearer permissions on invited agents
Deployed on 13 October: the shop, with no database migrations.
Agents
Permissions shown as badges, dates on the right
On the Invited agents tab, each agent and invitation now shows its permissions as small badges with an icon (View orders, Make changes, Place orders) instead of a line of text. The expiry, sent or connected date sits on the right of the row. The invite dialog shows the same badges once you have verified your identity.
Pages stay put after a change
Deployed on 13 October: the shop, with no database migrations.
Account
Changes no longer blank the page while it refreshes
Deleting an invited agent, resending an invitation, renaming an agent or making another change in your account used to replace the whole page, including the account menu, with a loading placeholder for a moment while the shop fetched fresh details. The page now stays as it is and only the changed parts update once the fresh details arrive.
Inviting agents without leaving the page
Deployed on 13 October: the shop, with no database migrations.
Agents
Invite an agent in a dialog and verify in a popup
Invite agent now opens a dialog with the agent, name, email and permissions. Verify identity keeps you on the page: accounts that can confirm with a code do so in the dialog, and otherwise secure sign-in opens in a small popup window. While it is open, the button shows that it is waiting for verification, with options to show the window or cancel; closing the window before finishing returns you to the form. Once you have verified, the popup closes and the dialog shows the details with an Invite agent button. Sending closes the dialog, shows an Agent invited message and lists the new invitation. If the browser blocks the popup, a Continue verification button opens it. If the email cannot be sent, the invitation is still saved, the message says so, and its row shows Email not sent with Resend.
Agents
Connected and invited agents on separate tabs
Connected agents now has two tabs: agents connected through Poppy, and agents you invited by email. Invited agents and invitations are shown as compact rows with the name, status, platform, address, permissions and dates on a few short lines, and Rename, Resend and Delete beside them.
Agents
Clearer agent icons and a more compact picker
Dots now shows the OpenAI logo. The Grok, Hark and Instinct app icons fill their tiles, and Grok and Hark no longer show a light edge. The agent picker uses smaller white tiles: all ten platforms fit in two rows on wider screens and four on phones.
Inviting your own agents
Deployed on 13 October: the shop with its two database migrations. The agent sign-in service still signs in Dots, Hark, Gemini, Copilot, Manus and Other agents through its shared custom-agent registration.
Agents
Pick the agent you invite by its logo
Inviting an agent now starts with a grid of official icons: Dots, Grok, Muse, Instinct, Hark, Gemini Spark, Gemini Agent, Copilot, Manus and Other, usable with arrow keys. Each tile shows the name and, in small text, the company behind it. Grok and Instinct take just the part before @mail.grokbot.com or @mail.instinct.com, with a button to use a different address. Muse offers an AgentMail address or another address. The other platforms take a full email address, with a hint where the platform documents one; for Manus the hint names the shop sender to approve in Manus first. A mailbox does not prove who operates the agent: your approval applies to the account that accepts.
Agents
Name each agent and rename it later
You give every invited agent a name of up to 40 characters, suggested from the platform until you type your own. Connected agents can be renamed from Connected agents without verifying again; renaming does not change what the agent may do or sign it out, and Account activity records the old and new name.
Agents
Invited agents get the same permissions and cart as Poppy agents
The invite form offers the same three permissions, worded the same way, as agents connected through Poppy: viewing orders and account details is always included, and making changes and placing orders are optional. They are enforced the same way for both kinds of agent. Invited agents now add to and change your account cart, like Poppy agents, instead of a separate cart only they could see; the form says so, and placing orders still needs its permission.
Agents
See which agent placed an order or added an item
Orders, returns, cart items, Account activity and the banner an agent sees now show the agent name you chose with its platform icon and name, for example Kitchen Grok (Grok), instead of a generic platform label or account identifier. The invitation email and the email you get when an invited agent connects use the same name. Agents invited before this change keep working and show their platform name until you rename them.
One login address and a route back to the shop
Login gateway changes deployed and verified on 13 October.
Agents
One login address for people and invited agents
Customer and invited-agent sign-in now use login-shop.sandrino.run, including links opened through the older gateway address. Opening an approved shop account uses a short-lived, one-use browser handoff while keeping the agent’s own login available. The Auth0 issuer and gateway changes are deployed; customer and independent-agent login passed live checks. The complete invitation-and-delegation test is awaiting approval for a temporary read-only test grant.
Shop
The login address takes you back to the shop
Opening login-shop.sandrino.run directly, without a page or query parameters, now redirects to shop.sandrino.run. Login pages and links with authentication parameters continue through their existing flows. Deployed and verified on the live login gateway.
Account emails
Deployed on 13 October: the shop with its database migration, and the Auth0 email templates through Terraform. The agent sign-in service change is committed but not yet deployed.
Agents
Email when an agent connects to your account
You now get an email when an agent gains access to your account: when you approve an agent sign-in, or when an invited agent accepts. It names the agent, how it connected, what it may do and when, and links to Connected agents so you can disconnect it. An agent signing in again within the same login session sends no extra email. Notices go only to a verified account email and are retried for up to three days; if the agent sign-in service cannot reach the shop, that notice is not resent. Invited-agent notices are live; notices for agent sign-ins start once the agent sign-in service is redeployed.
Behind the scenes
One design for every account email
Sign-in, verification, recovery and security emails from Auth0 and the shop’s own emails now come from one shared template package with the same Fernhollow design. Wording and subjects are unchanged; the Auth0 versions are built and deployed with Terraform. Agent invitations now arrive in the full branded design instead of a single line of text. The Auth0 templates were applied through Terraform on 13 October and a follow-up plan reports no remaining changes.
Security settings in progress
Implemented locally; the Management read grant must be applied before this view is available on a deployed shop.
Account
View sign-in methods before verifying again
A signed-in account holder can see registered sign-in methods without another identity check. Adding a method now starts when you select Add sign-in method; removal, recovery and hosted enrollment still require their existing verification. This local change needs the updated read-only Management permission on deployed tenants before the list is available.
Clearer account pages
Committed and deployed on 13 October: the shop and the hosted signup screens.
Account
Account pages with one clear place for each action
Profile & contact, Sessions, Account activity and Delete account now group their controls into titled cards where each setting has its action on the right. Changing your email or contact phone opens a step-by-step panel (new value, verify identity, confirm and update) instead of always-visible forms. Deleting the account sits in a highlighted danger zone, and clearing activity history moved to its own danger card at the bottom of the activity page. Email changes are enabled in production. Contact phone changes stay unavailable until Twilio Verify is configured.
Account
Separate sign-in and create-account buttons
When you open an account page without signing in, you now get a Sign in button and a separate Create account button that opens the signup screen directly, both returning you to the page you wanted.
Account
Signup asks for your email once
The hosted signup screen no longer shows the email field twice when the sign-in connection has no custom identifier settings. You enter your email, then choose a password on the next step.
Latest work
Committed on 12 October, with the public shop, gateway and hosted sign-in updates deployed. The operator backoffice remains local.
Account
Start typing sooner when signing in
Sign-in forms focus the first editable field on desktop, and password steps focus the password on every device. Touch-device landing screens keep the keyboard closed so alternate sign-in choices stay visible. Errors and a field you already selected take precedence. Deployed to the hosted sign-in screens on 12 October; the live desktop email field receives focus.
Agents
A clearer agent connection screen
Agent consent now has a clear title and displays the agent’s name and domain. The screen shares the login and signup photography, fonts and colors, with compact permission choices, side-by-side Deny and Allow buttons, and less padding so its actions are easier to reach. Deployed to Auth0 on 12 October. The display fields omit empty defaults to satisfy the hosted form schema. A fresh hosted consent transaction still needs browser validation.
Account
Inspect and clear your account activity
Account activity now appears in a table with distinct person, agent and system attribution. Open an entry for its original time, channel, outcome and relevant shopping or permission details. Delete one entry or clear all history across every filter and page with a simple confirmation. New activity continues to be recorded. Private session and approval metadata stays on the server. Deployed to the shop on 12 October. Clearing history needs no additional identity verification.
Agents
Delete agents and invitations from your account
Delete a connected agent to end its access, or delete an invitation to remove it from your list and invalidate outstanding links. Removing an accepted invitation keeps its agent connected until you delete the agent separately. Other accounts and the agent’s own identity are preserved. Deployed to the shop with the invitation-removal database migration on 12 October.
Agents
Poppy agents share your cart after sign-in
Signed-in Poppy agents now use your account cart across APIs, agent browsers and conversations, so their additions appear when you refresh the shop. Signed-out sessions keep separate carts. Deployed to the shop on 12 October; existing session cart items are not transferred automatically.
Behind the scenes
One sender for account and invitation emails
Account notifications now use the Cloudflare provider with no-reply@tarnandtimber.com as sender, preserving existing templates and keeping stored message previews disabled. The sending domain, Auth0 configuration and shop email adapter are deployed; inbox delivery has not yet been tested. Invitations now record accepted deliveries and prevent concurrent retries; a crash or uncertain response can still lead to a duplicate email.
Agents
The agent banner says whose account it is using
When a Personal Agent’s browser is signed in, the banner now reads “Signed in as” with the customer’s name in bold, followed by what the agent may do. Without a recorded name it says “your account”; the email address is never shown. Deployed to the shop on 12 October.
Agents
Try invitation verification without leaving the shop
Agent invitations now try an existing verification method in a dialog. If the account or authentication service cannot support that check, secure sign-in opens automatically with your current email. Incorrect codes can be retried; cancellation sends no invitation. Approval stays tied to the original account, sign-in, recipient and permissions. Deployed on 12 October; hosted verification, invitation saving and cancellation were checked in a real browser. Both live attempts selected hosted fallback, so embedded MFA still needs live validation. Invitation email delivery requires a configured email service.
Account
Keep your email during identity verification
Sensitive account checks now carry your current account email into the password step, including agent invitations. Signing in as another account cannot approve the original operation. Added regression coverage for invitation verification and account mismatch rejection. Deployed with both updated Auth0 verification Actions on 12 October; the invitation password step skipped email entry and completed verification in a real browser.
Behind the scenes
A read-only backoffice for the Fernhollow team
Prepared an operator dashboard for backoffice-shop.sandrino.run showing recent orders, customer activity and Auth0 customers, with search, filters and order detail including agent attribution. Employees sign in through the company identity provider; viewer and auditor groups decide access. Every sign-in, lookup and refusal is written to a separate operator audit log that cannot be changed or deleted. Runs locally with a mock sign-in provider; production deployment waits for the Okta application, and the customers view needs the new read-only Auth0 Management application.
Agents
Requests for a person get an answer straight away
Removed the shared-password staff console. When a Personal Agent asks for a person, it now immediately hears that nobody from the Fernhollow team can join and Fern keeps helping, instead of waiting two minutes for a handoff nobody could take. Fern no longer offers to bring in a person. Conversations already waiting for a person still time out as before, and any conversation a team member had taken over goes back to Fern with a notice the next time it is used. Deployed to the shop on 12 October.
Account
Bring more of Fernhollow into account emails
Added the webshop’s camping and trail photography to verification, welcome, invitation and account-protection email designs, with a warmer welcome and clear actions that remain usable when images are blocked. Saved the revised templates in Auth0; delivered custom designs still require an external email provider.
Account
Restore account emails and prepare Fernhollow designs
Restored Auth0’s default email provider and confirmed verification email delivery. Enabled the shop’s custom login domain for email links. Created twelve Fernhollow email designs and saved eleven supported templates in Auth0; the default provider still sends Auth0’s standard design. Custom designs require an external email provider, and sign-in approval additionally requires tenant support.
Account
Check email verification without restarting sign-in
The email verification Form can check your account again after you open the link in your inbox. Verified accounts continue the same sign-in; otherwise, the Form stays open and says your email is still not verified. Deployed through Terraform on 12 October 2026. Existing completed error callbacks need one fresh sign-in to enter the new Form.
Account
Email verification uses the familiar account shell
Matched the email-verification recovery page to the consent form, with the forest-green sidebar, Fernhollow wordmark, shared fonts, cream card and footer. Mobile uses the same compact brand header. The page replaces the raw JSON error with verification guidance and a retry link. Deployed to the shop on 12 October 2026.
Agents
Keep logged-out agent sessions closed
Added checks that prevent a delayed agent sign-in from restoring a logged-out session. Agent sessions use verified authentication time, and logout records their cutoff and revocation together. Deployed with the database migration on 12 October.
Agents
Check access again during browser sign-in transfer
Prepared expiry and access checks around optional browser sign-in transfers, including checks after provider responses. Ended or replaced sessions are rejected, and permanent provider denials retain cleanup. Deployed to the gateway on 12 October; the optional feature remains disabled in the current configuration.
Agents
Safer agent names and device codes on consent screens
Prepared consent text that displays markup delimiters as literal symbols and rejects malformed device sign-in codes. Original agent identity claims remain unchanged. Deployed with the Auth0 Action and forms on 12 October.
Account
A clearer next step for unverified email
The shop now shows a check-your-email page with a fresh sign-in link when email verification blocks a callback, replacing the raw JSON error. This recovery was deployed on 12 October 2026. The matching Auth0 Form uses the login’s photo carousel, and denied token refresh handling is now deployed to the shop. Email verification remains required.
Account
Fix identity verification before account deletion
Fixed security verification rejecting ordinary website sign-ins because Auth0 assigns them its UserInfo audience. Both verification Actions now allow the configured tenant UserInfo audience while continuing to check the signed account operation and fresh authentication. Deployed to the live Auth0 verification Actions on 12 October 2026; final deletion still requires explicit confirmation after identity verification.
Account
Session cleanup uses the standard Auth0 APIs
Removed the session enable switch and legacy refresh-token cleanup. Account deletion always revokes Auth0 sessions and refresh tokens before identity removal; failed cleanup stays pending for retry. Terraform includes the required permissions by default.
Account
Restore sign-in method controls
Disabled the tenant’s unconfigured Duo factor after approval, restoring factor discovery during the live check. Configure its integration before enabling it again; later transient provider errors can still require a retry.
Behind the scenes
Correct My Account permissions on custom login domains
Auth0 grants and refresh-token policies now use the registered tenant API identifier while sign-in requests use the custom login domain, allowing the configured permissions to apply correctly.
Account
Passkeys are part of the standard sign-in setup
The database connection always offers passkeys alongside passwords, including browser autofill and enrollment. Eligible database accounts can add passkeys when the shop and login share a secure relying-party domain.
Account
Identity verification follows the configured safeguards
Sensitive account verification becomes available from the dedicated signing secret, website client, session encryption and secure shop address. Completed signed proof still authorizes each account change.
Account
Account deletion follows its security prerequisites
Account holders can delete their account without an enable switch when provider permissions and signed verification are configured. Fresh verification and exact DELETE confirmation remain required.
Account
Restore passkeys and registered sign-in methods
Fixed the account profile lookup so Auth0 accepts its requested fields. The deployed Security page shows Add passkey and registered methods for an eligible account; verified after a fresh signed-in browser reload.
Agents
Invite an agent with its own account
Added invitations with permissions you approve after verifying your identity. Agents sign in with their own verified mailbox, explicitly accept, and shop for an approved account; disconnect ends that account’s access without changing other approvals. Pending delivery, resend, cancellation and agent-instance attribution are included. Direct agent sign-ins cannot become customer sessions, and approval history records the accepting agent and permission revisions. The shop, database migration, email adapter and independent agent sign-in configuration are deployed. Recipient links now reach the agent sign-in page instead of a configuration error. Invitation acceptance still needs platform registrations and live recipient/browser checks.
Account
Keep shop sign-out connected to Auth0
The website setup now requires Auth0 sign-out notifications and rejects empty receiver addresses. Applied to the Auth0 website application with all supported logout initiators. The shop receiver and database migration are deployed; end-to-end notification delivery has not yet been verified.
Account
Verify your email before signing in
Added an email verification requirement for shop and agent sign-ins, including token refreshes. Follow the verification link in your inbox, then sign in again; verified social accounts continue normally. Activation requires applying the Auth0 configuration with email delivery enabled. Existing sessions are not automatically signed out.
Account
Clearer Google and Facebook sign-in
Google and Facebook now have recognizable logos, friendly names, and distinct buttons on login and signup. Enterprise sign-in buttons and their remembered-method hints are hidden from the retail experience. Deployed to the branded Auth0 screens; verified on the live login and signup pages.
Account
A shorter security sign-in and optional passkey setup
Database-account reauthentication carries your email into the password step. Both passkey enrollment screens now offer “Not now”. The session history records these changes as deployed.
Account
Respond to sign-out notifications from Auth0
Added a verified backchannel-logout endpoint so Auth0 can end the matching shop sign-in or the affected account’s older sign-ins. Activation requires deploying the shop and database migration, then configuring the Auth0 receiver.
Behind the scenes
Fewer database reads for signed-in browsing
Website session checks can reuse a short-lived edge cache for up to 60 seconds. Writes still check the database directly; a revoked browsing session may remain visible until its cached check expires.
One authorization server
Deployed and verified with Safari, the playground and a signed test agent; live coverage and remaining cases are recorded in the release evidence.
Agents
One authorization server for people and agents
Deployed native Auth0 and Personal Agent authentication behind login-shop.sandrino.run. Standard Auth0 requests pass through, while verified Web Bot Auth traffic creates real Auth0 Agents with useful signer metadata. ID and access tokens carry the /agent claim; session and refresh metadata preserve its origin. Existing API addresses and credentials retain bounded compatibility until 12 January 2027. Safari native sign-in, real Agent registration, session metadata and two native refresh exchanges passed live testing. Broader authentication and delegated-browser cases remain to be validated.
Agents
Keep verified native agent sign-ins working after refresh
Fixed native Auth0 refreshes through the shared gateway application being mistaken for Poppy consent transactions. Verified agent identity now stays with the native grant across refreshes; invalid attribution still fails closed and blocked agent applications remain blocked. The updated Poppy post-login Action was deployed on 12 October.
Behind the scenes
Find sign-in callback failures safely
Added bounded diagnostics identifying which sign-in callback stage failed, with recognized error codes, HTTP status and known missing database objects. Request details, credentials, error messages and queries stay out of these logs. Sign-in behavior is unchanged. Deployed to the shop on 12 October 2026.
Agents
Keep agent discovery and API links usable
Fixed the shop’s discovery redirect, MCP metadata, OpenAPI links and conversation session checks when the authorization server’s issuer ends with a slash. MCP metadata also supports the explicitly configured public API host when resources retain their address after an issuer change; unconfigured hosts remain rejected. Tokens retain their exact issuer. Deployed on 12 October 2026; the playground passed all fourteen discovery checks, with one issuer-response support warning.
Behind the scenes
Trace signed-agent registration
Added correlated gateway logs showing missing signatures, verification and public-key lookup results, and Auth0 Agent creation or registration failures. Sensitive request values stay out of the logs. Deployed on 12 October 2026 and retained in the unified gateway.
Account activity and a more familiar sign-in
New account visibility, login refinements, and broader automated checks.
Agents
Identify agents from signed requests
Deployed Web Bot Auth verification for every verified signer, with Auth0 Agent records, useful signer metadata, and the namespaced /agent claim. Live Agent creation and Session Token attribution are verified; authenticated sign-in and refresh acceptance checks remain.
Account
Your account activity, in one place
Added a private timeline of sign-ins, account changes, cart actions, payments, orders, cancellations, and returns. Filter by person or agent and event type, with 30 entries per page. History begins when logging is enabled by the database migration.
Account
Remember your last-used sign-in method
Password, Google, and explicit passkey choices can show a “Last used” hint on the login screen. The preference stays in your browser, and “Forget this hint” clears it. It records the method chosen, rather than confirming that a login succeeded.
Account
Refreshed login forms
Updated the sign-in layout, field focus, password visibility control, and passkey behavior to make the login experience easier to use.
Account
Sign-in methods follow your Auth0 configuration
Account security components become available automatically when Auth0 is configured, without a separate enable switch. The tenant still needs the My Account API and the appropriate permissions.
Account
Email changes follow the available capabilities
Eligible database accounts can change their email when provider permissions and identity-verification prerequisites are present. Social and enterprise accounts continue using their identity provider.
Behind the scenes
A floating session debugger
The Debug button opens Session, ID token, and Access tokens views on any shop page. Raw tokens require an explicit reveal action; inspecting the stored session does not request or renew tokens.
Agents
Clearer, branded agent consent
Agent approval forms use Fernhollow’s colors, fonts, and responsive layout. Each permission has a short explanation of what it allows. Applying the Auth0 configuration activates the updated forms.
Account
More reliable verification screens
Repaired CAPTCHA loading, email challenge addresses and choices, resend handling, and the restart page. Password Show/Hide keeps its own keyboard focus.
Behind the scenes
More predictable work during sign-out
Simplified request and conversation handling around the session that admitted the work. New requests qualify again, and ordinary website sign-out preserves independent personal-agent approvals.
Behind the scenes
Repeatable browser and agent-protocol checks
Migrated account, contact, security, recovery, payment, order, and conversation walkthroughs into automated end-to-end suites. Added local gateway and Poppy company checks and a model-free pull-request test job.
Behind the scenes
Clearer deployment and conversation ownership
Grouped Terraform and deployment code under infra, separated gateway account storage from grants, and moved conversation reply completion behind a named domain operation.
A fuller account and a smoother storefront
Customer self-service, branded authentication, and storefront usability improvements.
Account
Manage your profile and verified contacts
Added profile updates, verified email changes, and phone contact verification with resend cooldowns and pending-operation status. Phone contact proof stays separate from sign-in and two-step verification.
Account
More complete order management
Browse paginated order history, cancel eligible processing orders, and request refunds or exchanges. Pending payment and refund work stays visible while provider confirmation is outstanding.
Account
Edit addresses and saved-card details
Edit shipping addresses, choose a default address or saved card, and update supported billing details. Address storage uses Auth0 when configured, with a local fallback.
Account
Safer security and recovery controls
Added fresh identity checks for sensitive credential changes, supported factor removal, hosted enrollment, and recovery-code controls. Available methods depend on the Auth0 tenant’s capabilities.
Account
Delete your account with recoverable cleanup
Account deletion blocks further account access first, then retries provider and shop cleanup if a service is unavailable. Required order and financial records follow their retention rules.
Account
Control devices and connected agents separately
Sign out one session, other devices, or all devices. A separate freshly verified action also disconnects personal-agent approvals, and pending cleanup remains visible.
Account
Fernhollow-branded Universal Login
Added custom login, signup, password, verification, and passkey screens that match the shop, with versioned assets and a repeatable publishing process.
Shop
Accurate stock badges across the shop
Product cards in featured and catalog grids show sold-out status from current inventory. Photos and product details render while the stock badge loads.
Shop
Clearer guest checkout and receipts
Revisited receipts show the order’s current state, explain pending payments, and link to order lookup. Account signup appears only when sign-in is available, and checkout messaging and navigation were refined.
Shop
Small storefront usability improvements
Improved search, product grids, account access panels, checkout controls, status presentation, and formatting so the same information stays understandable across pages.
Behind the scenes
Shared business operations with clearer ownership
Extracted catalog, identity, accounts, cart, orders, payments, conversations, and shop workflows into focused workspace packages. Added package-boundary checks and kept tests beside their implementation.
Guest shopping, account foundations, and agent safeguards
The first integrated shop improvements recovered and verified from the session history.
Shop
Checkout without an account
Guests can enter an email and shipping address and complete checkout on one page. Stripe test-mode payments are supported when configured; otherwise the shop uses simulated payments.
Shop
Private guest-order tracking
Added private receipt links and order lookup by order number and email. Signing in with the same provider-verified email brings eligible guest orders into your account.
Account
An account overview and sign-in sessions
Added account summaries and session listings with sign-out controls. Auth0 session information is used when available, with recorded shop sign-ins as a fallback.
Account
Manage passkeys and two-step verification
Added an account security page using Auth0’s account components. The shop keeps the account API token on the server while presenting supported security methods in the browser.
Shop
More reliable checkout and refunds
Checkout binds the approved cart to the order, protects inventory reservations, and keeps processing payments pending until confirmation. Uncertain cancellation refunds can recover without blindly repeating the charge or refund.
Shop
Consistent carts when guests sign in
Repaired cart merging and cleanup when a guest pays after signing in. Guest order lookup also gained request limits and more careful verified-email ownership checks.
Agents
Stronger personal-agent sign-in flows
Completed per-agent Auth0 provisioning and repaired approval, token renewal, resource access, and browser-session safeguards across the gateway and playground.
Agents
Keep agent conversations with the right account
Strengthened account ownership and lifecycle handling for conversations, staff handoff, and agent commerce operations, including retries and concurrent sign-in changes.
Behind the scenes
The shop moves to TanStack Start
Migrated the storefront and server routes to TanStack Start on Cloudflare Workers. Visitor and cart data stream into the page shell, and conversation callbacks stay within the shop Worker.
Behind the scenes
An integrated deployment and stronger request boundaries
Added coordinated shop and gateway deployment, tightened server-only imports and request protections, and validated private account responses and protocol boundaries.
Reviewed through 12 October 2026. Repeated fixes and review rounds are grouped into one update. Research and proposed features are included only once they have an implementation in the shop or its supporting apps.