An authorization check is only useful if the sensitive operation cannot accidentally run without it. In a typical TypeScript application, a route checks a user’s role and then calls a data function. Those two lines may sit together today, but a later refactor—or an AI coding agent—can move or omit the check while the code still compiles.
gdp-ts explores a way to make that relationship visible to the TypeScript compiler. It is an open-source library, linter, and optional coding-agent skill based on Ghosts of Departed Proofs. Its core idea is simple: a sensitive function should accept not just an ID, but evidence that the right fact was checked about that exact value.
This is a useful pattern to evaluate for applications with project roles, paid-feature entitlements, protected documents, or other permissions. It is not a substitute for the actual runtime decision.
The bug gdp-ts is designed to catch
Imagine a team dashboard where only project owners may rotate an API key:
const allowed = await isProjectOwner(viewerId, projectId);
if (!allowed) throw new Error("Forbidden");
await rotateProjectKey(projectId);
The runtime check is essential. But if rotateProjectKey accepts a plain projectId, another caller can invoke it without making the check. A background job, a new server action, or a copied route may bypass the rule unintentionally.
The dangerous variation is subtler: the caller checks ownership of project A and passes project B to the sensitive function. A boolean called allowed does not say which user and project were checked.
How named values and proofs work
The project README describes three steps:
- Name the values. Its
name()callback gives each runtime value a distinct compile-time identity, even when two values share the same underlying TypeScript type. - Prove a fact. A small trusted module performs the real check—perhaps a database role lookup—and returns a typed proof for the named user and project if it succeeds.
- Demand that proof. The sensitive function’s signature requires a proof tied to its exact arguments. A proof about another project, a missing proof, or an unchecked
nullresult should fail type-checking.
In conceptual terms, the API changes from rotateProjectKey(projectId) to rotateProjectKey(namedProject, ownerProofForThatUserAndProject). The check still happens at runtime. The extra type relationship helps the compiler reject a call where the check and the operation do not match.
The package also provides lint presets for ESLint and Oxlint. Those rules matter because TypeScript’s type system can be circumvented with assertions or by minting a proof outside the intended trusted module. The repository’s error examples show the kinds of mismatches its tests expect the compiler to reject.
Where this helps—and where it stops
The strongest initial candidates are high-impact operations with several callers: changing access settings, reading another tenant’s data, issuing a protected token, or enabling a feature that requires an active plan. Start with one operation and trace its current permission decision, rather than wrapping every function in a proof on day one.
There are important limits:
- The proof is only as correct as the runtime check that creates it. A wrong query or outdated policy can still grant the wrong access.
- Facts can change. A role may be revoked after a proof was created. Decide where fresh checks or short-lived transactions are necessary.
- Types are not a security boundary at runtime. Unsafe casts, JavaScript callers, unchecked inputs, and code outside the typed path still need attention.
- Lint, tests, and review remain necessary. Test denied cases, cross-project mix-ups, concurrency, and every production entry point.
The repository describes the runtime proof as a small object; the major benefit is the compile-time relationship, not cryptographic evidence. Treat the pattern as an additional guardrail alongside your existing authorization system—not as a replacement for one.
Trying gdp-ts in a TypeScript project
The repository installation instructions currently list TypeScript 5.4 or newer and pnpm add @gdp-ts/core. Its optional coding-agent skill can be installed with npx skills add rauchg/gdp-ts. Review third-party package and skill code under your normal dependency policy before adopting either.
For a controlled pilot:
- Pick one sensitive function and write down the exact subject, resource, and policy it requires.
- Keep the real permission check in a small trusted module that can produce the corresponding proof.
- Make the sensitive function demand that proof for the same named values.
- Turn on the supplied lint preset and add type-level negative tests for missing, mismatched, and forged proofs.
- Run your existing runtime and integration tests, including a revoked permission and a cross-tenant case.
- Review whether the added type ceremony is worth the reduction in accidental misuse before expanding it.
That evaluation should include maintainability. If developers cannot understand why a proof is required, a clever type system can become a source of workarounds rather than protection. Keep the trusted checks and naming conventions easy to find.
Building safer application workflows
The broader lesson is valuable even if you do not adopt this package: put security requirements at the boundary of the operation that needs them. In my website and web application development work, I use explicit authorization rules, tests, and reviewable boundaries for sensitive actions. For teams adding coding agents or automated workflows, AI consulting can help define which operations an agent may propose, execute, or escalate for approval.
If you want a starting point, examine the gdp-ts README and examples and try one authorization path in a branch. A passing type check is a useful signal; a production permission decision still deserves runtime evidence and human review.
Sources
Frequently asked questions
What is gdp-ts?
gdp-ts is an open-source TypeScript library, linter, and optional AI skill that uses named values and proof types to make sensitive functions require evidence of a relevant check.
Does gdp-ts perform authorization checks for my app?
No. Your application still performs the real runtime check, such as looking up a role or entitlement. gdp-ts helps connect that check's result to a sensitive function call at type-check time.
Can gdp-ts make an application secure by itself?
No. TypeScript types can be bypassed, proofs can become stale, and runtime behavior still needs tests, review, least-privilege permissions, and a sound authorization policy.
Can I add gdp-ts incrementally?
Yes. The repository recommends starting with a sensitive operation and its trusted proof-producing check, rather than converting the whole codebase at once. It requires TypeScript 5.4 or newer.
Is this an official Vercel product?
The linked repository is published under Guillermo Rauch's GitHub account. This article discusses that repository; it should not be read as a claim that gdp-ts is a generally available Vercel platform feature.
