contract
cache-purge specs/cache-purge/contract.kmd
Corpo da especificação
Cache-purge contract
This spec defines the cross-component contract that every Koder Stack implementation of "cache-purge HTML injection" MUST follow. It exists because the same mechanism is implemented in four places (Koder Jet v1.28+, the Go SDK middleware, koder_kit Flutter web, and koder_web_kit vanilla JS), and we need a single source of truth for which meta tag to use, what content values mean, and how idempotency is detected so implementations never disagree.
The contract is the meta tag. Everything else (snippet shape, gate strategy, transport encoding) is implementation detail and may diverge.
1. Meta tag
Every implementation that injects cache-clearing logic into an HTML
response MUST emit, somewhere inside <head> and BEFORE any logic that
clears caches:
<meta name="koder-cache-purge" content="<value>">
The name attribute MUST be exactly koder-cache-purge (lowercase,
no whitespace). Implementations that scan for the marker MUST be
case-insensitive on the name value but the canonical emission is
lowercase.
2. Reserved content values
| Value | Meaning |
|---|---|
self |
The application (or its framework) injected the snippet itself. The host is asserting ownership of the purge policy. Outer layers (jet, SDK middleware) MUST skip injection on responses carrying this value. |
injected-by-jet-121 |
Koder Jet 1.28+ performed the injection. The 121 suffix tracks the issue that established the convention. |
injected-by-sdk-go-002 |
The Koder Go SDK middleware (SDKGO-002) performed the injection. |
injected-by-kit-016 |
koder_kit (KIT-016) performed the injection (Flutter web build-time or runtime). |
injected-by-web-kit-006 |
koder_web_kit (WEBKIT-006) performed the injection. |
A new implementation MUST register a new content value here in the
form injected-by-<slug>-<ticket-id> before merging. Do not reuse an
existing value.
3. Idempotency rule
Any layer that would inject a cache-purge snippet MUST first scan the
response head buffer for <meta name="koder-cache-purge" ...> (any
content value). If found, the layer MUST skip injection — including
both the meta tag and the script.
The match SHOULD be case-insensitive on the name attribute and
quote-style tolerant (single quotes, double quotes, or unquoted are all
accepted). A regex equivalent to:
(?i)<meta\b[^>]*\bname\s*=\s*["']?koder-cache-purge["']?[^>]*>
is the canonical pattern. The reference implementation lives in
infra/net/jet/internal/middleware/cache_purge.go (cachePurgeMetaTagRe).
This single rule covers two scenarios with one mechanism:
- App self-declaration — the application or a higher-level SDK
(
koder_kit.declareCachePurgeSelf()) emitscontent="self"so outer layers (jet, server-side SDK middleware) defer to it. - Pipeline double-pass — a response that already passed through one cache-purge layer carries the marker; a second pass detects it and becomes a no-op. This makes the chain safe to compose in any order.
4. Snippet body (informative)
The cache-clearing JavaScript is not part of the contract. Each implementation may emit whatever IIFE it prefers, as long as it follows the meta tag in document order. The reference body used by Koder Jet:
<script>
(function(){
if ('caches' in window) {
caches.keys().then(function(k){ k.forEach(function(n){ caches.delete(n); }); });
}
if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations().then(function(r){
r.forEach(function(x){ x.unregister(); });
});
}
})();
</script>
SDKs MAY emit a different snippet (e.g. with subresource cleanup, with SDK-version annotations) provided it is functionally equivalent and runs synchronously enough to complete before the page begins making new fetches.
5. Insertion point
The meta tag + script SHOULD be inserted immediately before </head>.
Implementations MAY fall back to immediately before <body> when no
</head> exists. Implementations MUST NOT inject into responses
without either marker.
6. Encoding
Implementations MUST round-trip the response encoding. If the response
arrives compressed (gzip, br, zstd), the implementation MUST
decode → inject → re-encode with the same algorithm and adjust
Content-Length. When re-encoding fails, the implementation MUST drop
Content-Encoding and send identity, NEVER deliver a corrupt encoded
body.
7. Scoping & gates (informative)
Whether to inject for a given request is implementation-specific. The Koder Jet reference offers three orthogonal axes (domain glob, client IP allow-list, client IP deny-list with deny-wins-allow precedence) but SDKs may expose simpler or richer policy. The contract does NOT mandate a specific gate set — only the meta tag and idempotency.
8. Versioning
This spec is versioned by date in the date frontmatter field. Breaking
changes to the meta tag name or to the idempotency match pattern
require:
- Updating this spec with a new section "Breaking change YYYY-MM-DD"
- Updating every listed implementation in the same release window
- Adding a transition period during which both old and new markers are recognised
Reserved content values are append-only — never remove or repurpose.
Referências
infra/net/jet/backlog/done/121-cache-purge-injection-scoped.mdengines/sdk/go/backlog/pending/002-cache-purge-middleware.mdengines/sdk/koder_kit/backlog/pending/016-cache-purge-self-declare.mdengines/sdk/koder_web_kit/backlog/pending/006-cache-purge-helper.md