Architecture
How Pretense works
A local proxy that scans, mutates, transmits, and reverses -- in under one millisecond. Your code never leaves your machine in its original form.
Token Scanning
Pretense parses source code to extract every identifier -- functions, classes, and variables -- without touching comments or string literals.
The scanner uses a regex-based AST walker that understands TypeScript, JavaScript, Python, Go, Java, C#, Ruby, and Rust syntax. It identifies token boundaries and classifies each token by type (function, class, variable, constant). Comments are preserved verbatim because they carry developer intent that LLMs need. String literals are preserved because multiline strings cannot be safely mutated without breaking semantics.
// Fetch the user's payment token from the vault
async function getUserPaymentToken(userId: string): Promise<string> {
const vaultClient = new PaymentVaultClient(process.env.VAULT_URL);
return vaultClient.getToken(userId);
}getUserPaymentTokenfunctionmutateuserIdvariablemutatevaultClientvariablemutatePaymentVaultClientclassmutate// Fetch the user's payment token...commentpreserveprocess.env.VAULT_URLenv-refpreserveMutation Algorithm
Each identifier is hashed with SHA-256 and the first 4 hex characters become the mutation suffix. The result is deterministic, reversible, and meaningless to external observers.
Determinism is critical: the same identifier always produces the same synthetic, so the LLM sees a stable codebase across sessions. The 4-char suffix gives 65,536 possible values -- enough uniqueness for any real codebase. Prefixes (_fn, _v, _cls) preserve the token type so LLMs can still reason about the code structure without knowing the real names.
mutate("getUserPaymentToken", type="function"):
hash = SHA256("getUserPaymentToken") // full 64-char hex
short = hash.slice(0, 4) // "4a2b"
return "_fn" + short // "_fn4a2b"
mutate("userId", type="variable"):
hash = SHA256("userId")
short = hash.slice(0, 4) // "9x2c"
return "_v" + short // "_v9x2c"
mutate("PaymentVaultClient", type="class"):
hash = SHA256("PaymentVaultClient")
short = hash.slice(0, 4) // "8d3f"
return "_cls" + short // "_cls8d3f"getUserPaymentTokenfunction_fn4a2buserIdvariable_v9x2cvaultClientvariable_v7e1aPaymentVaultClientclass_cls8d3fProxy Transmission
The mutated source is sent to the LLM API. Secrets detected in transit are blocked entirely -- the request never leaves your machine if a credential is found.
Pretense runs a parallel secrets scan on every outbound request using 30+ regex patterns covering API keys, JWT tokens, AWS credentials, database connection strings, and PII. If a secret is detected, the entire request is rejected with a clear error. Mutated identifiers are syntactically valid code -- the LLM can read, reason about, and improve it as if it were real code.
// Fetch the user's payment token from the vault
async function _fn4a2b(_v9x2c: string): Promise<string> {
const _v7e1a = new _cls8d3f(process.env.VAULT_URL);
return _v7e1a.getToken(_v9x2c);
}Reversal
When the LLM responds, Pretense applies the mutation map in reverse. Every synthetic identifier is swapped back to the original. The developer never sees mutated code.
The reversal pass is a simple string substitution driven by the in-memory MutationMap built during the scan phase. Because mutation is deterministic, there is no ambiguity: _fn4a2b can only ever come from one source token. The reversal is byte-exact -- indentation, whitespace, and comments are untouched. The round-trip is lossless.
// Fetch the user's payment token from the vault
async function getUserPaymentToken(userId: string): Promise<string> {
const vaultClient = new PaymentVaultClient(process.env.VAULT_URL);
return vaultClient.getToken(userId);
}
// Pretense restored 4 identifiers from MutationMapWhy LLM output quality is preserved
Variable names are noise to LLMs. Models infer meaning from structure, types, comments, and relationships -- none of which Pretense touches.
Structural semantics preserved
Function signatures, class hierarchies, and call graphs remain intact. The LLM can still reason about architecture, interfaces, and control flow.
Comments never touched
Developer intent, TODOs, and business-logic annotations are passed through verbatim. LLMs use these heavily for context.
Types and interfaces intact
TypeScript type annotations, generics, and return types are preserved. The LLM understands the data model even with mutated names.
String literals unchanged
Error messages, SQL queries, and template strings pass through unmodified. No broken references or malformed queries.
function getUserPaymentToken(
userId: string
): Promise<string> {
// real function name
// real variable names
}function _fn4a2b( _v9x2c: string ): Promise<string> { // real function name // real variable names }
The LLM can still improve logic, fix bugs, and add features. It just never learns your naming conventions or domain vocabulary.
Rust hot-path
The scanner and SHA-256 hashing are implemented in Rust via a native Node.js addon. The Rust layer uses rayon for parallel work-stealing across all CPU cores, making it 27x faster than a pure JavaScript implementation on large codebases.
use rayon::prelude::*;
use sha2::{Digest, Sha256};
pub fn scan_tokens(source: &str) -> Vec<Token> {
// Split into lines, scan in parallel across cores
source.par_lines()
.enumerate()
.flat_map(|(line_no, line)| scan_line(line_no, line))
.collect()
}
pub fn mutate(identifier: &str, kind: TokenKind) -> String {
let hash = Sha256::digest(identifier.as_bytes());
let short = &format!("{:x}", hash)[..4]; // first 4 hex chars
let prefix = match kind {
TokenKind::Function => "_fn",
TokenKind::Class => "_cls",
TokenKind::Variable => "_v",
};
format!("{}{}", prefix, short)
}
// getUserPaymentToken (function) -> _fn4a2b
// PaymentVaultClient (class) -> _cls8d3f
// userId (variable) -> _v9x2cThe Rust addon ships as a prebuilt binary inside the npm package. No build toolchain required on the developer machine.
Security model
Pretense is local-first. Nothing about your source code is ever sent to Pretense servers. The mutation map lives in memory and on disk in your project directory.
| Data | Where it lives | Sent externally |
|---|---|---|
| Source identifiers (real names) | In-memory MutationMap + .pretense/ | Never |
| Mutated identifiers | Outbound LLM request | Yes -- to LLM API only |
| Comments and strings | Outbound LLM request | Yes -- verbatim |
| Detected secrets | Blocked before transmission | Never |
| Audit log | .pretense/audit.db (SQLite) | Never (Pro: cloud export optional) |
| Mutation map | .pretense/mutations.json | Never |
Next steps
Ready to try it? Get protected in 60 seconds.