Skip to content
MrJev

jevcache

Local decision cache keyed on (model, schema, state), with redaction and canonicalisation before hashing, for cheaper repeats and deterministic replay in CI. No license file at the time of writing.

View on GitHub →

Hands-on review

A local cache for Jev decisions, shipped as a binary with no source and no licence. It drops user_id before hashing, so two users share one answer.

Good for

  • A genuinely useful idea: treat a decision as a pure function and memoise it
  • Dropping timestamps before hashing, which is the right call
  • `publish` really does export fingerprints and answers only, never your state
  • A published SHA-256 that matches the binary, which we checked

Watch out for

  • No licence at all — the README's 'free to use' is not one
  • `user_id`, `order_id` and `session_id` are stripped before hashing
  • The local ledger keeps raw state, mode 644; the masker misses tokens and passwords

Tested Sep 20, 2026 at v0.1.0 binar · debian:13-slim in Docker with --network none, against the built-in mock backend

How we reviewed this: we ran the published v0.1.0 Linux binary in debian:13-slim with --network none, against its own JEVCACHE_BACKEND=mock, and checked its SHA-256 against the published .sha256. We made no Jev calls. There is no source to read.

What it does

Treat a Jev decision as a pure function of (model, schema, state) and cache it locally, so the second identical decision costs nothing. That is a good idea, and the right one for a model priced per input token where the same question gets asked thousands of times.

Before hashing, the state is canonicalised: PII patterns are masked and “volatile” fields are dropped, so cosmetically different states that mean the same thing land on one key. Dropping created_at and timestamp is exactly right.

It drops the identifier the decision is about

We built a one-question schema and two states that differ in a single field:

$ echo '{"user_id":"alice","amount":100}' > alice.json
$ echo '{"user_id":"bob","amount":100}'   > bob.json

--- decide alice ---
{"answers":{"risky":{"value":true,…}},"cached":false,"fingerprint":"4e2c6b3363ac…"}
--- decide bob ---
{"answers":{"risky":{"value":true,…}},"cached":true, "fingerprint":"4e2c6b3363ac…"}

Identical fingerprints. Bob’s decision was never computed — "cached":true means it came from Alice’s row. Changing amount instead produces a different fingerprint, so the canonicaliser is working as designed; user_id is simply on the list of things it throws away.

We probed the list. Against a fixed base state, adding each field and watching the fingerprint:

field added fingerprint
(base) 21dca6618083
user_id, id, uuid 21dca6618083
session_id, request_id, trace_id 21dca6618083
order_id 21dca6618083
created_at, timestamp, updated_at 21dca6618083
idempotency_key c80feff6a382
name 8a2bd81a1ca2
amount 8ad234e13558
email d84b9680fb4c

Ten fields vanish. Three of them — user_id, order_id, session_id — are not volatile metadata; they are frequently the subject of the decision. “Is this user allowed to do this” and “should this order be held” are the questions people cache.

The project advertises the behaviour. jevcache demo:

  ticket T-1001  decide()  ->  "billing"  (0.90)   routed to the model
  ticket T-2087  decide()  ->  "billing"  (0.90)   cache HIT  ·  0ms  ·  $0

  a different ticket id, but the same decision — the volatile id was redacted before hashing.

For that example it is defensible: two tickets with the same text should route the same way, and the ticket number is noise. The trouble is that one rule covers user_id too, and there the identifier is the signal. Nothing warns you, nothing lets you pin a field, and the answer arrives flagged as a hit.

The README’s claim is narrower than it reads. “PII … and volatile fields (ids, timestamps) never enter the key” is true and is about the key. It is not a statement about what gets sent or stored, and it is easy to read as one.

The local ledger keeps the secrets the masker misses

~/.jevcache/ledger.log records every decision with a state_preview holding the raw state. We passed a state with four planted values and then grepped the ledger:

planted value in the ledger
email: a@b.com masked
ssn: 555-12-3456 masked
authorization: Bearer SUPERSECRET present, plaintext
password: hunter2hunter2 present, plaintext

The masker is pattern-based — emails, phone numbers, long digit runs — so it catches the shapes it knows and passes everything else through. A bearer token and a password have no distinctive shape, so they land in the file intact. The file is mode 644, readable by every account on the machine.

This is the reading of the README that matters. “PII … never enter the key” is a claim about the key, and it is true. It is not a claim about the ledger, and the ledger is where your state actually comes to rest.

There is no licence

README.md:122 has a ## License heading, under which it says jevcache is “free to use”. The repository contains no LICENSE file and GitHub’s API reports license: null.

“Free to use” is a sentiment, not a grant. With no licence, default copyright applies and nobody has permission to redistribute the binary or ship it in a product. For a tool whose whole pitch is dropping into someone’s production decision path, that is the first thing to fix, and it costs one file.

There is also no source

Three commits. README.md and og.png. The product is a prebuilt binary distributed through GitHub Releases — the SHA-256 matches the published checksum, which is good practice, and we verified it. But nothing here can be read, audited, forked or patched, and everything above we had to establish by black-box probing.

--help also lists a subsystem the README does not mention:

jevcache market | usage | earnings | wallet [deposit <micros>]

The README’s roadmap says “Next: … per-recall pricing so publishers can charge”. It has shipped.

Smaller things

hit is backwards. A miss returns "cached":false,"hit":true; a hit returns "cached":true,"hit":false. cached is the field to trust.

jevcache add merges a cache file or URL with no signature and no provenance check, and the README’s only defence is “A bundle is plain JSON — inspect it before you add it”. Because identifiers are stripped, fingerprints are predictable, which makes a bundle that asserts a chosen answer for a chosen state easier to construct than it should be.

One thing the project gets right and deserves credit for: publish really does export fingerprints and answers only. We checked the bundle — no state, no previews. Whatever leaks locally stays local.

Verdict

48 stars, three commits, no source, no licence, no tests, no CI, one open issue. The idea deserves a real implementation; this is a landing page with a binary attached.

We’ve reported the licence and the identifier stripping, and asked whether the source is coming. If you want decision caching today, the pattern is a dozen lines around your own client, and you get to decide yourself which fields identify the subject. Compare jevcal, which caches with its source in the open.

See how it compares with other tools in Best Jev tools, tested hands-on.

Review updated Sep 20, 2026. Numbers quoted from the project are its author's own; we don't publish our own measurements of Jev.

More in Libraries & Integrations

agentrun

★ 423▲ 13

Parcha-ai/agentrun

A workflow DSL for agents with a Jev package for the steps that are decisions rather than generation.

TypeScript

switch

★ 420▲ 170

osolmaz/switch

TypeScript framework for decision-model harnesses: your program offers a short list of next steps and the model picks one at a time until it can answer, with chess, wikirace and rubric-judge examples run on Jev.

TypeScript

Ten Levels of Jev

★ 264▲ 10

disler/ten-levels-of-jev

Thirty worked uses arranged in ten levels, from one if statement to an agent that reaches for Jev itself, with a video walkthrough.

TypeScriptReviewed

Get new Jev projects every week

New Jev releases, pricing changes, and the best new projects, once a week. No spam; unsubscribe anytime.

Powered by Buttondown. See our privacy policy.