Skip to main content
ClaudeWave

TypeScript slice of candor-spec — the derivability proof: written from the spec alone, 20/20 on the shared conformance oracle.

ToolsOfficial Registry0 stars0 forksJavaScriptApache-2.0Updated today
ClaudeWave Trust Score
95/100
Verified
Passed
  • Open-source license (Apache-2.0)
  • Actively maintained (<30d)
  • Clear description
  • Topics declared
  • Documented (README)
Last scanned: 9/14/2026
Get started
Method: Clone
Terminal
git clone https://github.com/tombaldwin/candor-ts
1. Clone the repository.
2. Follow the README for installation and usage instructions.
Use cases

Tools overview

# candor-ts

<p align="center"><img src="https://raw.githubusercontent.com/tombaldwin/candor/main/assets/beaky.svg" alt="Beaky, the candor canary" width="180"></p>

**candor for TypeScript: per-function side effects, transitively, with a deterministic policy
gate.** candor-ts resolves every call through the TypeScript compiler API and reports, for each
function in your project, which effects it can reach — `Net`, `Fs`, `Db`, `Exec`, `Env`, `Clock`,
… — **including effects inherited through any chain of calls across files**, with a disclosed
`Unknown` wherever resolution fails (a callback value, an `any`-typed callee — never silently
pure). A [candor-spec](https://github.com/tombaldwin/candor-spec) implementation, sibling of the
[Rust](https://github.com/tombaldwin/candor-rust) and
[JVM](https://github.com/tombaldwin/candor-java) engines.

**Site:** [candor.poly.io](https://candor.poly.io) — the measured case in five minutes.

```sh
npm install   # typescript + @types/node

node scan.mjs <project-dir>                 # tsconfig.json honored; tests excluded; writes
                                            #   <dir>/.candor/report.json + .callgraph.json
node scan.mjs . --policy .candor/policy     # the §6.2 gate: exit 1 on violation, 2 if unreadable
node scan.mjs . --gate-json gate.json       # + the structured verdict {spec, ok, violations} (§3.3)
CANDOR_BASELINE=saved.json node scan.mjs .  # AS-EFF-005 guard: exit 1 if an existing fn GAINED an
                                            #   effect vs the saved report; 2 if it can't evaluate

node scan.mjs --version                     # installed build + spec contract (offline), + upgrade line

node query.mjs show     .candor/report db.save 1   # a function's effects (match ladder)
node query.mjs where    .candor/report Net 1       # direct sources vs inheritors
node query.mjs callers  .candor/report db.save 1   # the blast radius (transitive callers)
node query.mjs map      .candor/report 1           # module → effects overview
node query.mjs containment .candor/report          # §6.1 boundary-effect dispersion (+ baseline = ratchet)
node query.mjs blindspots  .candor/report          # the Unknown SOURCES, ranked by blast radius
node query.mjs whatif   .candor/report db.save Net policy  # pre-edit gate verdict (exit 1)
node query.mjs diff     .candor/report baseline 1  # per-function effect delta (exit 1 on a gain;
                                                   #   a baseline from a DIFFERENT build ⇒ disclosed ⚠ + exit 0)
node query.mjs gate --report dep/.candor/report.json --policy arch.policy --gate-json -
                                                   # ⟨0.24⟩ apply a policy to an EXISTING report, NO scan
                                                   #   (exit 0/1/2) — the supply-chain verb: gate a
                                                   #   DEPENDENCY's published report without its source.
                                                   #   Reads that report and nothing else; a rule the wire
                                                   #   cannot answer (`forbid`, `allow`, a class-scoped
                                                   #   `deny` with the scoping field absent) is REFUSED
                                                   #   with exit 2, never evaluated on partial evidence.
```

A checked-in **`.candor/config`** (spec §3.4) replaces the env wiring — `policy arch.policy` /
`baseline <report.json>` / `deps <report paths>` one per line, discovered by walking up from the
scan target; relative values resolve against the config's repo, so CI is "point at the repo". A
configured-but-unusable config/policy/baseline fails loud (exit 2), never silently gateless.

The scan-time **baseline guard** (AS-EFF-005, spec §7) makes effect *regressions* un-shippable:
point `CANDOR_BASELINE` (or the config's `baseline` key) at a saved report, and any existing
function that **gained** an effect fails the scan — exit 1, the records join the `--gate-json`
verdict. New functions are exempt (reviewed as new code, not a regression). The guard is
fail-closed like the policy gate: a present-but-unparseable baseline, or one produced by a
different engine build (§2.1 — an engine upgrade is baseline-invalidating), exits 2 **without
evaluating**; only a genuinely absent file is a one-line note (guard not active). Keep the two
surfaces straight: `query diff` is the read-only comparison — it *discloses* a producing-build
mismatch (⚠, exit 0) and informs; the scan-time guard is the gate-grade fail-closed surface, the
one CI should hold. Semantics mirror the reference engine (candor-java) exactly.

**Staying current:** check your installed version and upgrade — [candor/AGENTS.md §2a](https://github.com/tombaldwin/candor/blob/main/AGENTS.md#2a-staying-current--check-the-version-upgrade). `npx -y candor-ts --version` prints the build, the spec, and the upgrade one-liner (offline; candor never phones home).

Function names are module-qualified with `.` segments (`src.db.save`), so policy scopes read
naturally. A function declared inside a TS `namespace` carries the namespace segments in `fn` and
the callgraph keys (`src.util.Ns.helper`) — so layer policies on namespaces bite — while the §2
`hash` join key keeps the bare local name; builds before 0.8.7 omitted the segments, so crossing
that line invalidates saved baselines (regenerate them). A `pure <scope>` rule forbids every
*effect* but not `Unknown` — the §4 trust marker is uncertainty, not an effect (matching the
reference engine, candor-java); `deny Unknown <scope>` is the explicit knob for boundaries that
must also exclude the unverifiable case.

```text
# .candor/policy
deny Net domain                       # the domain layer reaches no network, even through helpers
pure  parse                           # parsing is effect-free
allow Db in db  orders audit_log      # the db layer touches ONLY these tables
allow Net in billing api.stripe.com   # billing talks ONLY to Stripe
forbid domain -> infra                # the domain layer must not depend on infra
```

The report carries the four **literal surfaces** where a declaration makes them decidable —
`hosts` at `Net` calls, `tables` at `Db` calls (SQL table positions, mirroring the Rust/JVM
extractors exactly, **plus TypeORM's `@Entity("user")` declarations** read through the receiver's
`Repository<T>` type argument), `cmds` at `Exec`, path-shaped `paths` at `Fs` — never from a
runtime-computed value, propagated transitively, enforced by the `allow` rules above. On a real
Nest app this makes table-level policy live: `allow Db in article.service article comments` flags
the service reaching `user` and `follows`.

**The classifier** is curated (the same under-report-and-say-so posture as the other engines): the
Node builtins (`fs`, `net`/`http`/`tls`, `dns`, `child_process`, `worker_threads`, `node:sqlite`,
`node:vm`, `process.env`, the clock), the **web network globals** (`fetch`, `XMLHttpRequest`,
`navigator.sendBeacon`, and `WebSocket`/`EventSource` — construction, `send` and `close`, charged
identically whether they resolve through `lib.dom` or through `@types/node`'s `undici-types`
re-export), the HTTP/queue/mail tier (axios/got/node-fetch/undici/ws/
socket.io/nodemailer, gaxios + googleapis-common + google-auth-library, stripe, @sentry/*,
posthog-node, bull/bullmq), the database drivers (pg/mysql2/mongodb/redis/ioredis/sqlite3/
better-sqlite3/knex) **and the ORM tier** (TypeORM — with `@Entity("…")` table extraction —
Prisma, Mongoose, Sequelize, drizzle-orm), plus execa/cross-spawn/shelljs/open, fs-extra/
graceful-fs/rimraf/glob/chokidar, dotenv, winston/pino/bunyan. An unlisted npm package contributes
nothing — candor never guesses an effect — but the scan **names it**: the receipt's coverage-ledger
line (marker: `classifier doesn't cover`) lists every package the code demonstrably calls that
candor's classifier neither classifies nor has reviewed-pure, and each function carries the
`invisible` list it (transitively) reaches.

⟨0.32⟩ **Node core is the one part of the classifier that is a denylist, not a curated list.** The
builtins are a finite set, so every module's surface is reviewed and a core member that is neither
classified nor reviewed effect-free reads `Unknown[native:<module>.<member>]` — never pure. That is
why `v8.writeHeapSnapshot()` is `Fs`, `inspector.open()` is `Net`, and `repl.start()`,
`process.dlopen()` and `new worker_threads.Worker(file)` are visible holes rather than silence.

## MCP server — candor as agent ground truth

`candor-ts-mcp` exposes the read-only queries as an [MCP](https://modelcontextprotocol.io) server, so
a coding agent can ask **"if I change this, what's the runtime blast radius?"** or **"what reaches the
network?"** and get deterministic ground truth from a precomputed report — instead of burning tokens
tracing the call graph by hand (the measured ~700–2000× token win on blast-radius questions).

```jsonc
// in an MCP client config — point it at a report you've already scanned
{ "command": "npx", "args": ["-y", "candor-ts-mcp"],
  "env": { "CANDOR_REPORT": ".candor/report.myPkg.scan" } }
```

Tools: `candor_impact` (backward blast radius), `candor_reachable` (what runs at runtime),
`candor_where` (effect surface), `candor_path` (how an effect is reached), `candor_callers`,
`candor_show`, `candor_map`, `candor_containment`, `candor_blindspots`, `candor_whatif` (pre-edit
gate check — a given-but-unreadable policy is a loud error, never a clean verdict), `candor_gate`
(the checked-in `.candor/config` policy verdict), `candor_diff`/`candor_gains` (baseline deltas).
Each takes an optional `report` prefix (else `$CANDOR_REPORT`); `--root <dir>` locks the server to
one workspace. The server is **query-only** — it never scans (the analyzer self-boundary, spec
§7.12: an agent or a hook produces the report; the server reads it, Fs only). The query logic is
the shared `query-core.mjs`, the same answe
architecture-as-codecode-analysisdeveloper-toolseffect-systemnodejsstatic-analysistypescript

What people ask about candor-ts

What is tombaldwin/candor-ts?

+

tombaldwin/candor-ts is tools for the Claude AI ecosystem. TypeScript slice of candor-spec — the derivability proof: written from the spec alone, 20/20 on the shared conformance oracle. It has 0 GitHub stars and its last recorded update is dated 2026-09-13.

How do I install candor-ts?

+

You can install candor-ts by cloning the repository (https://github.com/tombaldwin/candor-ts) or following the README instructions on GitHub. ClaudeWave also provides quick install blocks on this page.

Is tombaldwin/candor-ts safe to use?

+

Our security agent has analyzed tombaldwin/candor-ts and assigned a Trust Score of 95/100 (tier: Verified). See the full breakdown of passed checks and flags on this page.

Who maintains tombaldwin/candor-ts?

+

tombaldwin/candor-ts is maintained by tombaldwin. The last recorded GitHub activity is dated 2026-09-13, with 0 open issues.

Are there alternatives to candor-ts?

+

Yes. On ClaudeWave you can browse similar tools at /categories/tools, sorted by popularity or recent activity.

Deploy candor-ts to your cloud

Ship this repo to production in minutes. Each platform spins up its own environment with editable env vars.

Maintain this repo? Add a badge to your README

Drop the badge into your GitHub README to show it's tracked on ClaudeWave. Each badge links back to this page and reflects the live Trust Score.

Featured on ClaudeWave: tombaldwin/candor-ts
[![Featured on ClaudeWave](https://claudewave.com/api/badge/tombaldwin-candor-ts)](https://claudewave.com/repo/tombaldwin-candor-ts)
<a href="https://claudewave.com/repo/tombaldwin-candor-ts"><img src="https://claudewave.com/api/badge/tombaldwin-candor-ts" alt="Featured on ClaudeWave: tombaldwin/candor-ts" width="320" height="64" /></a>

More Tools

candor-ts alternatives