Seven objects. Seven separate promises.
Updated October 25, 2026 · Independent experiment
Each object writes one generated test value when you click it. Opening the desk only observes existing values. You can use one object at a time to narrow an experiment.
The lamp: page memory
The light is an in-memory JavaScript variable. Reloading the document normally resets it. If the lamp is off but the bag returns, the page restarted while some origin storage survived. That observation alone does not explain why the page restarted.
The ink and the guest pass: two cookies
The ink is a script-readable cookie. The guest pass is set by our same-origin endpoint with HttpOnly, so page JavaScript cannot read its cookie value directly. The endpoint echoes only CarryLab's generated marker. On the production HTTPS site both cookies are Secure and SameSite=Strict, with a one-day maximum age.
HttpOnly controls script access; it is not a persistence setting and does not establish login validity. An empty pass can reflect absence, expiry, browser policy or a different origin. A failed endpoint request appears as unavailable.
The bag: localStorage
The bag holds a small string in this origin's localStorage. It can survive a reload and be visible to another tab at the same origin. Browser clearing, privacy settings, partitioning and different profiles can change that result. CarryLab does not inspect another site's storage.
The draft: sessionStorage
The open draft belongs to the page session. It commonly survives reload but not an independently created fresh tab. A tab opened with an opener may initially receive a copy; after that the stores are separate. Our CLI state-file experiment can explicitly restore the value, which is different from ordinary tab lifetime.
The postcard: IndexedDB
The postcard is one entry in an object store. We wait for the write transaction to complete before counting it as present. An unavailable or blocked database is shown separately from an empty one. Observing a new desk does not create a database solely to obtain an empty result.
The map: Cache Storage
The map is a synthetic text Response saved through the Cache API. No service worker is registered, and this does not make the whole website work offline. A present entry proves only that this named cache returned the fixture response.
Read the four outcomes literally
Present means an exact match for the current trial. Empty means no fixture was read. Different means something was read but did not match, often because you started a new trial. Unavailable means an operation failed. Unknown values are omitted from exports rather than copied into a report.
The bag is empty. Did you return to the same desk?
A restored storage file can be correct while the page you opened belongs to another origin. Before blaming state-save or state-load, compare the complete starting URL, final URL after redirects, browser profile and trial identifier. A familiar logo or matching path is not a storage boundary.
- Same path, another port
http://localhost:3000/andhttp://localhost:4000/have separate localStorage areas. Starting a second development server does not make it inherit the first server’s bag.- Same host, another scheme
http://example.test/andhttps://example.test/are different origins. A redirect to HTTPS can change which localStorage area the final page reads.- Another host label
http://localhost:3000/andhttp://127.0.0.1:3000/are different origins even when they reach the same machine. Treat them as separate destinations in a reproduction.
These are origin examples, not links to a hosted CarryLab mirror. The localStorage reference describes its origin scope. Cookie scope follows different rules, so a cookie observation cannot stand in for a localStorage observation.
A four-observation experiment for your own development app.
- At origin A, write one harmless marker through your app’s normal interface. Record the exact final URL and marker name. Avoid credentials and real customer data.
- Reload origin A in the same profile. Read the marker without clicking the write control again. This separates persistence from the app quietly recreating the value.
- Open the same path at origin B, changing only the port. Record the value or absence there. An empty result at B says nothing about whether A’s storage was restored.
- Return to A and read again. If A still has the marker while B does not, the observations are consistent with origin separation. If A is also empty, investigate clearing, profile changes, write failure or restoration before drawing a conclusion.
Record “unavailable” separately when the storage operation throws or browser policy prevents access. An error is not an empty store. Do not use a file: page as a convenient substitute for the local server: its storage behavior is not a reliable origin experiment.
Apply the same discipline to CarryLab.
Keep the generated trial URL when reopening the desk. Starting a new trial changes the expected marker, so the same stored value may correctly become “different.” Observe first, save the report, and only then write a new value. Otherwise the act of checking repairs the state and destroys the evidence of what survived.
This exercise diagnoses destination and observation mistakes. It does not expand what agent-browser includes in a state file. Use the versioned native results to check that separate question.
Reference: MDN on sessionStorage, localStorage, IndexedDB, Cache Storage and cookie attributes.