Executable Knowledge: Cases Episode 01

No-cache is not no-store

Three cached pages and one private account page. Two HTTP directives that sound alike, and an answer for each that shows the rule it came from.

5 min5 cases

Jump to an Arxo answer

AI-generated podcast with synthetic hosts and AI-generated music. Every verdict and its stated grounds come from a recorded Arxo run; the facts, full answers and hashes are below.

Transcript

Plain text

SarahRoger, a question for every web developer listening. You put no-cache on a response. Did you just tell the cache not to keep it?

RogerMost people think so. Today we find out what you actually told it.

RogerQuick housekeeping first. We're both synthetic voices, the music is generated too, and so is this show. The answers aren't invented, though. Every result you'll hear comes from a recorded Arxo run, and the facts and hashes for each one are in the show notes.

SarahShow notes. Got it.

RogerThis is Executable Knowledge: Cases.

RogerPicture yourself setting cache headers for a site. Two directives look almost identical: no-cache and no-store. The rules come from RFC 9111, the HTTP caching standard. We loaded the two sections that matter into a small Arxo package, and we'll hand it five cases.

SarahWhat does a cache do by default?

RogerEvery response here is a plain GET that came back 200. By default the cache may keep it, and reuse it while it's fresh. The package even calls that rule reuse while fresh.

SarahStart easy, then.

RogerThe home page. Fresh, and sent without any directives.

Arxo answerResult: true. Rule applied: Reuse While Fresh.

SarahA yes, from the rule you just named.

RogerNow the news page. Also fresh. But it was sent with no-cache.

SarahAnd this is where I'd say: no-cache, so it isn't cached at all. Right?

Arxo answerResult: false.

RogerNo reuse. So you're half right. But listen to how it lost.

Arxo answerReuse While Fresh is defeated by No Cache Needs Validation, by priority.

SarahSo the reuse rule actually fired... and then lost?

RogerIt fired, and it was defeated by priority. And the rule that beat it says what it wants, right in its name.

Arxo answerRule applied: No Cache Needs Validation.

SarahNo cache needs validation. So not never. Just not without asking first.

RogerExactly. Not without checking with the origin server first.

SarahSo what happens when the cache does check?

RogerThat's the prices page. No-cache again, but this time the cache has validated the response with the origin.

Arxo answerResult: true. Rule applied: Reuse After Validation.

SarahBack to yes.

RogerAnd this one stacks two priorities. Listen.

Arxo answerNo Cache Needs Validation is defeated by Reuse After Validation, by priority. Reuse While Fresh is defeated by No Cache Needs Validation, by priority.

RogerValidation beats no-cache. No-cache beats plain freshness. That's all no-cache ever said: reuse, but only after validation.

SarahOkay. Then what's no-store for?

RogerA different kind of answer entirely. A proxy receives a private account page, sent with no-store. Nothing gets switched off here. The package sets up a prohibition, addressed to the proxy, from the day the response arrived.

Arxo answerProhibition: store response, on the proxy. Status: active.

SarahActive. So nothing has gone wrong yet.

RogerNothing yet. Last case. Same proxy, same page, and a record showing the proxy stored it anyway.

Arxo answerProhibition: store response, on the proxy. Status: violated.

SarahViolated.

RogerViolated, and the answer names who was bound by it: the proxy. That's the difference in one line. No-cache is about when a cache may reuse a response. No-store makes storing it a breach.

SarahSo no-cache means not without asking. And no-store means someone is on the hook.

RogerBefore you pick a directive, name the action you want to control: keeping the response, or using it again. Five cases, each with the rule that decided it. The facts, the full answers and the hashes are in the show notes.

SarahNext time: castling through a square nobody checked.

RogerBoth voices synthetic, every answer from a real run. This was Executable Knowledge: Cases, from Arxo.

The cases behind the answers

Each answer was produced by a recorded Arxo run. Open a case to see the facts it was given, the full answer and the hashes that pin the run.

home (fresh, no directives): may be reusedTRUE_ONLY

Result: true. Rule applied: Reuse While Fresh.

Facts given

  • get_200(urn:demo:http:home)
  • fresh(urn:demo:http:home)
  • get_200(urn:demo:http:news)
  • fresh(urn:demo:http:news)
  • no_cache(urn:demo:http:news)
  • get_200(urn:demo:http:prices)
  • fresh(urn:demo:http:prices)
  • no_cache(urn:demo:http:prices)
  • validated(urn:demo:http:prices)

Hashes

resultHash
sha256:37f616609157665df0904580c18e0f007f323ad50a6e55e5dda8a61f97c5cf8a
proofHash
sha256:a9e4c5c3924d4cf68aa1531dcb46bd24e69c2c91db7f75e224326c5c4fee51cf
caseHash
sha256:3efc6cf2838ff6fec7a125223c5f5acf9cdc4a9bbc5b2f9b85a338ecf0cb2968
programHash
sha256:0d5c34a3a5448085830f57b45a78799b4834f249b14277b7c3af622904811c8c
semanticHash
sha256:0cf2216f830cd57d486c8abe018a5aa2df833bf246392f4043e846d914ef7ebd
news (no-cache, not validated yet): may not be reusedFALSE_ONLY

Result: false. Rule applied: No Cache Needs Validation. Reuse While Fresh is defeated by No Cache Needs Validation, by priority.

Facts given

  • get_200(urn:demo:http:home)
  • fresh(urn:demo:http:home)
  • get_200(urn:demo:http:news)
  • fresh(urn:demo:http:news)
  • no_cache(urn:demo:http:news)
  • get_200(urn:demo:http:prices)
  • fresh(urn:demo:http:prices)
  • no_cache(urn:demo:http:prices)
  • validated(urn:demo:http:prices)

Hashes

resultHash
sha256:c6f10c29e22c49b47e5abfe52b060110005c5ebbe3d6d1b0fcb303b620ad187f
proofHash
sha256:efeaa2cb58e2ad29d863bab76180d4906fce872f08e63602934d423be50e398e
caseHash
sha256:3efc6cf2838ff6fec7a125223c5f5acf9cdc4a9bbc5b2f9b85a338ecf0cb2968
programHash
sha256:0d5c34a3a5448085830f57b45a78799b4834f249b14277b7c3af622904811c8c
semanticHash
sha256:0cf2216f830cd57d486c8abe018a5aa2df833bf246392f4043e846d914ef7ebd
prices (no-cache, validated with the origin): may be reusedTRUE_ONLY

Result: true. Rule applied: Reuse After Validation. No Cache Needs Validation is defeated by Reuse After Validation, by priority. Reuse While Fresh is defeated by No Cache Needs Validation, by priority.

Facts given

  • get_200(urn:demo:http:home)
  • fresh(urn:demo:http:home)
  • get_200(urn:demo:http:news)
  • fresh(urn:demo:http:news)
  • no_cache(urn:demo:http:news)
  • get_200(urn:demo:http:prices)
  • fresh(urn:demo:http:prices)
  • no_cache(urn:demo:http:prices)
  • validated(urn:demo:http:prices)

Hashes

resultHash
sha256:b1fb3617d5e7dd028591a8203d3c2df42d6c1957edbcb1aa210edb18e17c5324
proofHash
sha256:bb53c5551bf63ece694ecfb4ae77a8f4ece9be293ea5808c7fce5a703fe97c73
caseHash
sha256:3efc6cf2838ff6fec7a125223c5f5acf9cdc4a9bbc5b2f9b85a338ecf0cb2968
programHash
sha256:0d5c34a3a5448085830f57b45a78799b4834f249b14277b7c3af622904811c8c
semanticHash
sha256:0cf2216f830cd57d486c8abe018a5aa2df833bf246392f4043e846d914ef7ebd
no-store: the prohibition on storing is activeACTIVE

Prohibition: store response, on the proxy. Status: active.

Facts given

  • received(urn:demo:http:proxy, urn:demo:http:account, 2026-01-10)
  • get_200(urn:demo:http:account)
  • no_store(urn:demo:http:account)

Hashes

resultHash
sha256:8bb1751002cff7a3a71a93e4a9fecbe0ffb23a478fec63027d5e5fc6762132e2
proofHash
sha256:371d736d6aa4b767ec1b7ef741cb8b5b4bd0a5f85fce9e1e61a7575f82fc9d4e
caseHash
sha256:19b2a3ed25d0e966da92f4339bd456db226626a7088844dfbd93fa74ffb61510
programHash
sha256:0d5c34a3a5448085830f57b45a78799b4834f249b14277b7c3af622904811c8c
semanticHash
sha256:b5fcba2a9c37bd38098c876c5f354f8b1ee7d24c34682fc50340966699bf6b6e
no-store, and the proxy stored it anyway: violatedVIOLATED

Prohibition: store response, on the proxy. Status: violated.

Facts given

  • received(urn:demo:http:proxy, urn:demo:http:account, 2026-01-10)
  • get_200(urn:demo:http:account)
  • no_store(urn:demo:http:account)
  • stored(urn:demo:http:proxy, urn:demo:http:account)

Hashes

resultHash
sha256:9bfdd0a02d3c2d8fd713fe87b7d23f595f939253aed20ccf7c6be2b131133306
proofHash
sha256:ccb95130f82e50bbfd72be192695e877ebe371545c384cf2cad7803f8c2ad135
caseHash
sha256:03d7790954770f61cf87bc7dc6b18bd2bc8151d7cd73eca7ce56d289b6aee999
programHash
sha256:0d5c34a3a5448085830f57b45a78799b4834f249b14277b7c3af622904811c8c
semanticHash
sha256:97736655c65996fea311ea62c2df51a6b613d071e6219cd655d62880aeb01805

Run it again

Engine semantics law.core/0.2.7. Source: RFC 9111, sections 5.2.2.4 (no-cache) and 5.2.2.5 (no-store), in the Arxo playground example "HTTP cache directives".

./law engine lower apps/site/play-examples/http-cache/http-cache.law > program.lawir.json
./law engine evaluate-test apps/site/play-examples/http-cache/tests/http-cache.lawtest --test <N> --program program.lawir.json --out run/