ArchSutra AI

ArchSutra AI / Handbook

Technical specification

Module-by-module description of the implementation, data structures, algorithms, limits and extension points.

Version 0.1.0 · Source: docs/handbook/03-technical-specification.md

1. Stack#

AspectChoice
LanguageTypeScript 5.x, strict: true
Module systemESM ("type": "module", NodeNext)
RuntimeNode.js >= 20
Runtime dependenciesnone
Dev dependenciestypescript, @types/node
Test runnerbuilt-in node --test
Buildtsc to dist/; prepare builds on install from Git
Entry pointdist/cli.js (bin archsutra)

2. Repository layout#

src/            TypeScript source (8 modules + cli)
skills/         canonical Agent Skills (SKILL.md + references/)
agents/         canonical agent role contracts
schemas/        findings.schema.json
templates/      ADR template
fixtures/       labeled fixtures for evaluation (brownfield-monolith, clean-service)
examples/       committed sample outputs
scripts/        eval.mjs, build-docs.mjs
test/           archsutra.test.mjs, eval.test.mjs
docs/           handbook (source), generated HTML site, RFC, ADRs, proof
install/        thin install.sh / install.ps1 / install.cmd wrappers around npx
.github/        CI, CodeQL, Pages, templates, Dependabot

3. Modules#

3.1 types.ts#

Shared types: Finding, Evidence, Corpus, ArchMap, AuditResult, ScoreEntry, Severity, Confidence, EvidenceKind.

3.2 fsutil.ts#

  • walk(root, opts): breadth-limited recursive directory read. Skips ignored directories (node_modules, .git, dist, build, target, vendor, .venv, coverage, .terraform, ...), binary extensions, files over maxFileBytes, and all symlinks (uses Dirent.isSymbolicLink, never stat on links). Stops at maxFiles or timeBudgetMs and sets truncated.
  • safeJoin(root, rel): resolves and rejects any path outside root.
  • readLines(file): reads once, caches, returns null for binary (NUL byte) or unreadable files.

Defaults: 5000 files, 512 KiB per file, 15 000 ms.

3.3 redact.ts#

Pattern set: AWS access key, GitHub tokens (gh[pousr]_, github_pat_), sk- style keys, Slack tokens, Google API keys, private key headers, and quoted credential assignments (password, secret, api_key, token, access_key). detectSecrets(line) returns kinds only. redact(text) masks values. excerpt(line, max) redacts then truncates. Every evidence excerpt goes through excerpt.

3.4 map.ts#

  • moduleOf(rel): module name is group/name for packages|apps|services|libs|modules|internal|cmd, src/name for src/<dir>/..., else the top directory, else (root).
  • Imports: JS/TS relative specifiers via regex on from, import(), require(); Python absolute imports whose first segment matches a module. Edges are aggregated per module pair with a count and a first-seen example (path, line, redacted excerpt).
  • findCycles: Tarjan strongly connected components; components with more than one module are cycles.
  • Manifest parsing: package.json (frameworks, datastores, main), requirements.txt and pyproject.toml (package names).
  • Output ArchMap includes ecosystems (sorted by file count), frameworks, datastores, infra, entry points, modules (sorted by lines), edges, cycles, blind spots, coverage.

3.5 audit.ts#

Single pass over files for line rules, then structural checks. Rule evaluation order is irrelevant; findings are sorted by severity then rule ID, and IDs are assigned as <RULE>#<NNN> by final position. Full rule list: rule catalog.

Design notes:

  • Test and fixture paths (test/, tests/, __tests__/, fixtures/, *.test.*, *.spec.*, *_test.go|py) are excluded from injection, TLS, IaC and secret rules to reduce false positives.
  • Documentation files (.md, .txt) are excluded from line rules.
  • A private-key header counts only when base64 key material follows, so documentation strings and regex literals do not trigger it.
  • Credential lines containing example, placeholder, changeme, your_, xxxx, process.env, os.environ or getenv are ignored.
  • Scorecard: critical if any critical, weak if any high, adequate if any medium, else strong; Reliability, Performance, Scalability, Data integrity and Cost are always not assessed.

3.6 report.ts#

Pure functions that render Markdown, Mermaid (flowchart), findings JSON (schemaVersion: "1.0.0"), SARIF 2.1.0 (rule list plus one result per evidence location, severity mapped critical/high to error, medium to warning, low/info to note), and the artifact register with SHA-256.

3.7 verify.ts#

Parses frontmatter (simple key: value), validates skills and agents, resolves relative links outside code fences, checks Mermaid structure, validates findings.json.

3.8 adapters.ts#

  • HOSTS: declarative host table (skills directory, agents directory and suffix, AGENTS.md block flag, notes, docs link, support level).
  • planInstall: loads canonical content, renders per-host agent files, classifies each target file as create / update / unchanged / conflict using the install manifest hashes.
  • install: writes files, backs up conflicts under .archsutra/backup/<timestamp>/, updates the manifest.
  • uninstall: removes a file only if its current SHA-256 equals the recorded one; prunes empty directories; removes the managed block in AGENTS.md delimited by ` and `.
  • doctor: Node version, target existence, detected hosts, manifest drift/missing, canonical skills present, duplicate AGENTS.md blocks.
  • renderAgent: Claude gets name, description, tools: Read, Grep, Glob. Copilot gets description and tools: ['read', 'search']. No other keys are ever written.

3.9 cli.ts#

Hand-written argument parser (flags with values: output, host, target, fail-on). Commands dispatch to the modules above. Exit codes: 0 ok, 1 findings or conflict or failed doctor, 2 usage, 3 needs a model, 4 verification failed, 5 internal error. See the CLI reference.

4. Data formats#

4.1 Finding (JSON)#

{
  "id": "AS-SEC-006#003",
  "rule": "AS-SEC-006",
  "title": "TLS certificate verification disabled",
  "status": "open",
  "category": "security",
  "severity": "high",
  "confidence": "high",
  "evidence": [{ "path": "src/services/http.ts", "startLine": 2, "endLine": 2, "kind": "observed", "excerpt": "..." }],
  "impact": "...", "whyItMatters": "...", "fix": "...", "verification": "...",
  "acceptanceCriteria": "...", "effort": "S", "ownerType": "application developer",
  "caveats": "...", "references": ["https://owasp.org/Top10/..."]
}

Schema: schemas/findings.schema.json. Stable key is rule; the numeric suffix reflects position in one run and may change between runs.

4.2 Install manifest#

{ "version": 1, "installs": { "claude": { "files": { ".claude/skills/threat-modeling/SKILL.md": "<sha256>" }, "agentsMd": false, "at": "<ISO time>" } } }

5. Algorithms and complexity#

StepComplexityNotes
WalkO(files)bounded by maxFiles
Line scanO(total lines x rules)lines over 2000 chars skipped
Import graphO(lines)regex per line
Cycle detectionO(V + E)Tarjan on module graph
Install planningO(files in skills + agents)hashes only changed targets

Memory: file contents are cached per run (bounded by 5000 x 512 KiB worst case, in practice far less). clearTextCache exists for long-lived processes.

6. Error handling#

  • Unreadable directories/files are skipped silently and do not abort the scan.
  • Unparseable package.json is reported in failedChecks.
  • Fatal errors print archsutra: <message> and return exit code 5; --verbose adds the stack.

7. Extension points#

To addTouchRequired tests
Audit rulesrc/audit.ts (and rule catalog)seeded true positive, false-positive case, fixture expectation
Language import graphsrc/map.tsmap test with cycle
Host adapterHOSTS in src/adapters.tsinstall/uninstall roundtrip (automatic for every host)
Agentagents/<id>.mdverify enforces headings
Skillskills/<name>/SKILL.mdverify enforces frontmatter
Output formatsrc/report.ts and export in src/cli.tsschema/structure test

8. Build, test, release#

npm ci
npm run typecheck
npm test            # build + node --test (20 tests)
npm run verify      # archsutra verify .
node scripts/eval.mjs
node scripts/build-docs.mjs

CI runs the same on Ubuntu, Windows and macOS for Node 20 and 22, then a self-audit with --fail-on high and a check that production dependencies are empty.