Sajimora™ compares the wire shapes Java (Spring MVC / JAX-RS) and TypeScript (NestJS, Express/Fastify, Next.js App Router) endpoints produce against the contracts React / Angular (TypeScript) consumers call, then reports a deterministic verdict in the CI pipeline you already run — or fails closed when the evidence is missing. Zero mock files. Zero running Docker containers. Zero specification drift.
Experience Sajimora™ in Real Time
Toggle between a breaking refactor and a safe commit to watch Sajimora's™ AST engine react instantaneously.
public class DashboardDTO {
private Long id;
// ⚠️ DEVELOPER REFACTOR: Renamed 'lowStock' to 'lowStockAlerts'
- private int lowStock;
+ private int lowStockAlerts;
public int getLowStockAlerts() {
return this.lowStockAlerts;
}
}
mvn test. All Java unit tests passed 100% because the Java getter matches. The developer opened a PR unaware that the frontend dashboard will crash at runtime.
import { DashboardDTO } from '../types/dashboard';
export const fetchDashboard = async (): Promise<DashboardDTO> => {
const res = await api.get<DashboardDTO>('/api/dashboard');
// 💥 RUNTIME DISASTER: res.data.lowStock is now undefined!
if (res.data.lowStock > 5) {
showInventoryWarning();
}
return res.data;
};
$.lowStock. In production, users see a blank screen: TypeError: Cannot read properties of undefined.
sajimora verify --fail-on-violation
Completed in 1.91s
GET /api/dashboardlowStockAlerts)Air-gapped, zero-telemetry CLI qualification available for enterprise engineering teams.
Why hasn't this problem been solved?
Because every previous tool imposed massive manual developer friction.
Requires developers to write, mock, and maintain dedicated contract JSON/DSL files for every single endpoint call.
Relies on human developers to keep schema annotations and YAML specs up-to-date. In real-world enterprise repos, documentation silently drifts from true wire behavior.
Directly parses native Java and TypeScript ASTs using Tree-sitter. Derives wire contracts automatically without any human input.
How one CI step does the whole job
Pure static AST analysis. No JVM spinning up. No headless browsers. No mocked servers.
Tree-sitter extracts @RestController paths, Jackson @JsonProperty names, Lombok getters & generic DTOs.
Extracts Axios, Fetch, & Angular HttpClient calls, URL templates, and expected response interfaces.
JCS RFC-8785 Canonical Diff
PR check turns green. Zero added breaking wire changes. PR is completely safe to merge.
PR check blocked. Pinpoints exact file, line number, endpoint, and broken frontend client.
Parallel multi-threaded Rust threads walk Java controllers, records, Lombok accessors, and TypeScript Axios/Fetch calls file by file — on the ThingsBoard corpus named in the benchmarks below (1,354 production .java files in the served module, 5,479 across every module its build declares, and 2,280 .ts files under ui-ngx/src) the backend extraction takes 6–8 s and the frontend about 10 s on a laptop CPU.
Resolves class inheritance, constructor properties, type aliases (RestOf<T>, Partial), nullability, and Spring Pageable wrappers.
Canonical JSON (RFC-8785) contract envelopes are hashed and signed with Ed25519. Repeated runs on the same commit produce byte-identical artifacts.
Compares candidate changes against the baseline. Only new breaking wire changes fail the build; legacy debt is safely quarantined.
Measured on real public repositories
Thousand-endpoint backends, not hello-world apps. Every figure names the corpus that produced it.
• Sealed Artifact Reproduction: Re-measured 2026-10-25 against the checked-out public repositories at their current revisions, with one canonical walker over the sealed artifacts. Each figure comes from a run recorded in scripts/measurement/runs/ with its repository commit, command line and artifact hash, and each artifact reproduced byte-identically across two consecutive runs.
• Leaf Positions & UNKNOWN Reporting: A "leaf position" is one wire node reachable from a contract — following every type reference and counting an unresolvable reference as unresolved — so "resolved" is the share of those positions that are not UNKNOWN, and an UNKNOWN always carries the reason it stayed unresolved.
• Signature & Traversal Integrity: Endpoint counts are distinct method + path signatures; the artifact stores one record per direction, which is why the record count is roughly double. Traversal-complete means the consumer walk finished, not that a response shape was found.
When you refactor a service method signature, dozens of unit tests break. Sajimora uses native Tree-sitter AST rewriting to auto-repair test call sites in milliseconds — with 100% determinism.
Never waste engineering hours updating broken test calls after method refactorings. Sajimora updates call sites with type-safe default inference, atomic file writes, and zero side effects.
heal(heal(x)) == heal(x)
Autonomous verification and test synthesis running 100% locally with zero LLMs and zero hallucinations.
Synthesizes property tests (jqwik & fast-check) proving idempotence, null-safety, and domain boundaries.
Pure Rust Tier 1 constraint branch solver with strict 2-hop Demeter mock budget. Zero phantom tests.
Differential bisimulation gate: detects regressions between Git revisions with SHA-256 witnesses.
How verification works when frontend and backend live in separate repos
Monorepos verify in a single step. For polyrepos, Sajimora replaces slow staging environments with sealed cryptographic contract artifacts.
On every backend commit or release tag, the Java pipeline runs static AST export to generate a canonical contract envelope:
➔ Emits backend-contract.json sealed with an Ed25519 signature and SHA-256 JCS digest.
The sealed contract envelope is a plain JSON file — 12 MB for the 1,669-record ThingsBoard backend above (841 distinct endpoints), and it scales with the number of wire shapes rather than with lines of source. It is published to your existing infrastructure:
In the React or Angular PR pipeline, Sajimora compares the backend envelope it pulled against the client contracts extracted from this repo. On the ThingsBoard pair above that is 690 client contracts against 841 distinct endpoints and took 26.6–26.7 s at a 346 MB peak, on the laptop named in the FAQ below:
➔ Flags broken client calls without running the Java backend, database, or Docker. --trusted-local accepts unsigned artifacts and stays advisory; with --trust-policy a signed producer list governs admission instead.
Air-Gapped by Design. Zero Telemetry Phone-Home.
Built specifically for financial institutions, defense contractors, and healthcare organizations whose source code cannot touch external SaaS servers.
Distributed as a single compiled native binary. Operates completely offline inside private, firewalled runners.
No tracking pings. No usage counters. No background daemons. The binary makes zero outbound network calls by default.
Contract envelopes are canonicalized and signed locally using Ed25519. A modified envelope fails verification at admission time.
Untracked or ambiguous boundaries abstain as UNRESOLVED rather than guessing or fabricating claims.
The Path to Universal AST Contract Governance
From Java and TypeScript to enterprise-wide multi-language API verification.
Production static AST extraction calibrated on enterprise monorepos and polyrepos.
sajimora heal)
Expanding language extractors and multi-cloud artifact registry synchronization.
Universal multi-protocol contract verification and deployment safety gating.
net/http, gin, fiber)
We personally pair with your platform or backend leads to configure Sajimora in your GitHub Actions or GitLab CI in 15 minutes. Free 30-day trial: every rate we quote names the corpus and command that produced it, and anything the engine cannot prove is reported as UNKNOWN instead of passed.
Frequently Asked Questions
Never. Sajimora is a single compiled native Rust binary that links no HTTP or socket client, so it has no way to send your source, AST nodes, or results anywhere. It runs inside your private GitHub Actions runner, GitLab CI job, or local terminal.
Sajimora fails closed: a call whose URL or response type it cannot resolve stays UNKNOWN with the reason attached, and the artifact carries a TS_UNRESOLVED_CLIENT_URL blocker instead of a guess — 16 of them on the ThingsBoard ui-ngx corpus above. Known wrapper shapes (Axios config-object calls, one-hop HTTP helper modules, Angular HttpClient) are extracted directly. What is not configurable today is a custom base URL prefix or your own helper name list; the only config file is .sajimora/config.json, whose single supported key (polyrepo.localSiblings) tells the MCP lane where sibling checkouts live.
Backend: Java Spring MVC / JAX-RS (Spring Boot 2.x and 3.x, Java Records, Lombok getters, Jackson serialization), NestJS, Express/Fastify, and Next.js App Router (JSON route handlers).
Frontend: TypeScript and JavaScript applications running React or Angular (Axios, Fetch API, Angular HttpClient), plus declarative Java HTTP clients (Feign / HTTP Interface / MicroProfile REST Client).
Not extracted: Micronaut, Play MVC, Vert.x, Spark, Struts2, JSF, Spring WebFlux functional routes, RSocket, gRPC, GraphQL, Next.js Pages Router, Koa, Hapi, Hono, Restify, Polka and Elysia. A detected framework is named in a blocker; it is never silently reported as an empty complete service.
It is static analysis only — no JVM boot, no container, no running service — so it is one more step rather than a new pipeline. On the ThingsBoard corpus above, extract-backend took 6.2–8.2 s and peaked at a 112 MB working set, and extract-frontend took 9.9–10.4 s and peaked at 182–184 MB. Those are two consecutive warm runs of each command; the backend step walks every module the root pom.xml declares — 5,479 .java files, of which 1,354 sit in the served application module — so that it can resolve a DTO that lives in a sibling module, while serving endpoints only from the module you asked it to verify. The frontend step walks 2,280 .ts files under ui-ngx/src. Machine: AMD Ryzen 5 5625U, 12 logical cores, 15.3 GB RAM, Windows 11 (build 26200). Every artifact was byte-identical across its repeated runs. Reproduce it with
sajimora extract-backend --repo <thingsboard> --java-root application/src/main/java --service-id thingsboard --out backend.json.
We publish timings only with the machine, corpus and command that produced them, because a bare second count is not reproducible.