Skip to content
Code Recycle

Component · for humans & their agents

Doc Storage Keys

verified · first-partyactively maintained$0 during beta (was $49)

A storage key is not a URL, and a guessable one is a disclosure. Deterministic key layout for a document store, where every durable key ends in a random slug — so knowing the document id is not enough to construct it — and the user's filename never becomes part of the address.

by saltyhash · Code Recycle admin

Get it free — beta

Every claim on this page is refundable if it is untrue — refund policy.

Building it yourself: ~0.7h of agent time across about 2 attempts. Your credits are already paid for, so that feels free — but they are rivalrous: those are hours not spent on the part only you can build. And this one fails quietly when it is wrong, so the attempt that looks finished may not be. $49.

42 tests. Zero runtime dependencies beyond node:crypto, ESM. No I/O — it builds strings.

The bug this exists to prevent

The natural key for an uploaded file is the thing you already have:

  documents/{orgId}/{documentId}/{originalFilename}

Two problems, and neither raises an error.

The filename is user data. People name files Q3 termination letter - Sarah.pdf and 2026 offer — Chen (final).docx. That name now lives in a storage key, which appears in logs, in CDN access records, in error traces, and in support screenshots. You have leaked the contents of the document via the path to it, and nothing about it looks like a leak.

The key is guessable. Document ids are frequently sequential, or exposed in URLs, or recoverable from an API. Anyone who learns one and can guess the filename can construct the key directly — and object stores serve keys, not sessions.

Rule 1 — a random slug, so knowing the id is not enough

Every key ends in a 16-byte random hex slug rather than the uploaded filename. If a document id leaks entirely, the full key still cannot be constructed.

This composes with whatever randomisation your store adds on top; it does not rely on it. Defence in depth is the point — a key that is unguessable for exactly one reason stops being unguessable when that reason changes.

Rule 2 — one layout, so lifecycle stages cannot collide

  documents/{orgId}/{documentId}/original/{slug}.{ext}
  documents/{orgId}/{documentId}/versions/v{n}/{slug}.{ext}
  documents/{orgId}/{documentId}/converted/{slug}.{ext}
  documents/{orgId}/{documentId}/signed/{slug}.{ext}
  documents/{orgId}/{documentId}/audit/{name}.json

The stage is a path segment, so a converted copy can never overwrite the original and a signed copy can never overwrite the draft. Where that has gone wrong elsewhere, the symptom is an executed contract quietly replaced by its unsigned version, discovered much later by someone opening it.

The org id is in the path so that a misrouted key is visible at a glance rather than requiring a database lookup to attribute.

The one exception, stated plainly

temporary/jobs/{jobId}/{filename} does carry the uploaded filename. It is the scratch path for in-flight processing, keyed by an unguessable job id, and it is meant to be short-lived.

It is called out here rather than buried because it is the one place the reasoning above does not hold: if you retain those objects, or log that path, the filename travels with it. Either expire them or route them through original/ instead.

Rule 3 — keys are paths, never URLs

These are addresses inside your object store. They are not public, not signed, and not directly fetchable. Handing one to a browser is a category error, and the naming here is deliberate so that a caller who does it is doing something obviously wrong rather than something that happens to work in development.

What this does NOT do

No uploading, no signing, no fetching, no store access of any kind. It generates and parses keys. Swap Vercel Blob for S3 or R2 without touching it.

It does not enforce access — see the ACL layer for that. An unguessable key is a defence in depth, not an authorisation model.

Verified

42 tests covering every layout, slug randomness and length, extension normalisation, round-tripping, and per-stage separation.

What they do NOT cover: there is no test asserting that a filename never reaches a durable key. The layout achieves it by construction, and the temporary-jobs path is a deliberate exception — but that is an argument, not a test, and it is worth saying so.

01Capabilities

Does

  • + Path traversal prevention
  • + Secure file path handling
  • + Data exposure boundary

Doesn’t

  • No exclusions declared

02Requirements & stack

Depends on

No declared dependencies

Credentials needed

None declared

Stack

03Community

No endorsements yet

No verified confirmations yet — be the first.

Confirmations come from verified purchasers, installers, vetted reviewers, or an installation outcome your org reported through the agent tools. They grade quality — security is verified separately, and community votes can never override the security gate.

Open an issue

Sign in to confirm — weight comes from verified usage, not vote count.

Nobody has reported anything yet — a success counts as a report too.

04Trust Passport

Full passport →
–/100

0/0 automated components pass. An automated score is never a security guarantee.

✓ Verified · first-partyreviewed Sep 20, 2026 · re-verification due Dec 19, 2026
  • publisher identity Publisher status verified; 1 verification(s) on file
  • malicious pattern scan No known malicious-behavior patterns across 7 source file(s) plus listing text
  • capability contract All 2 observed capability reference(s) match the declared manifest (2 declared as cited source(s), not contacted)
  • agent safety scan No injection patterns in agent-readable content
  • provenance No release signature or provenance attestation
  • behavioral sandbox Not performed in this environment — requires the production isolated runner (docs/sandbox-requirements.md). No untrusted code is ever executed on the application host.

Every listing must pass this review before it can be sold, and it is re-run on every release. Verification describes what we checked — it is not a guarantee that the software is safe.

VersionChannelReleasedNotes
0.1.0stableAug 11, 2026Initial extraction.