Suture Architecture v5.3.1 (Post-Audit)
System Overview
Suture is a patch-based version control system with semantic merge capabilities. It understands the structure of files (JSON, YAML, CSV, XML, DOCX, XLSX, PPTX, OTIO, SQL, PDF, images, and more) and merges them intelligently using format-aware drivers rather than treating them as opaque text.
High-Level Component Diagram
User Interface Layer
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│suture-cli│ │suture-tui│ │suture-lsp│ │ VS Code │ │ Tauri │
│(clap, 58 │ │(ratatui, │ │(tower- │ │Extension │ │Desktop │
│ commands)│ │ 7 tabs) │ │ lsp) │ │(TS) │ │(HTML UI) │
└────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │ │ │
└──────────────┴──────┬───────┴──────────────┘ │
│ │
┌────────────────┼────────────────┐ │
│ │ │ │
┌──────▼──────┐ ┌──────▼──────┐ ┌───────▼───────┐ │
│ suture-core │ │suture-driver │ │suture-protocol│ │
│ CAS, DAG, │ │ trait + │ │ wire format, │ │
│ patches, │ │ registry │ │ compression │ │
│ repo engine │ │ (18 drivers) │ │ delta encoding│ │
└──────┬──────┘ └──────┬──────┘ └───────┬───────┘ │
│ │ │ │
┌──────▼──────┐ │ │ │
│suture-common│ │ │ │
│Hash, types │ │ │ │
└─────────────┘ │ │ │
┌──────────────┼────────────────┘ │
│ │ │
┌─────────▼─────────┐ │ ┌─────────────────────┐ │
│ suture-platform │ │ │ suture-hub │◄─────┘
│ Web UI, auth, │ │ │ HTTP/gRPC server, │
│ billing, merge │ │ │ SQLite, auth, │
│ API, orgs │ │ │ webhooks, mirrors │
└─────────┬─────────┘ │ └──────────┬──────────┘
│ │ │
┌─────────▼─────────┐ │ ┌──────────▼──────────┐
│ Stripe │ │ │ suture-raft │
│ billing │ │ │ consensus, election│
│ webhooks │ │ │ log replication │
└───────────────────┘ │ └──────────┬──────────┘
│ │
┌──────────────────┼────────────────────┘
│ │
┌──────▼──────┐ ┌──────▼──────┐
│ suture-s3 │ │suture-daemon│
│ blob store │ │ file watch, │
│ AWS SigV4 │ │ auto-sync, │
│ MinIO compat│ │ SHM, FUSE │
└─────────────┘ └──────┬──────┘
│
┌──────▼──────┐
│ suture-vfs │
│ FUSE3 mount │
│ WebDAV │
└─────────────┘
Data Flow: Merge Request Through the System
1. User edits files 2. suture add / commit
┌─────────────┐ ┌──────────────────┐
│ Working │──────────────▶│ Stage changes │
│ Tree │ │ Create patch │
└─────────────┘ │ (BLAKE3 hash) │
└────────┬─────────┘
│
3. Patch stored in CAS ▼
┌──────────────────┐ ┌──────────────────┐
│ BlobStore │◀──────│ PatchDag │
│ .suture/objects/ │ │ Add node + edges │
│ (content-addrs) │ │ Acyclicity check │
└──────────────────┘ └────────┬─────────┘
│
4. Push to remote (optional) ▼
┌──────────────────┐ ┌──────────────────┐
│ suture-hub │◀──────│ suture-protocol │
│ HTTP/gRPC │ │ Zstd compression │
│ V2 handshake │ │ Delta encoding │
└──────────────────┘ └──────────────────┘
5. Merge
┌──────────────────┐
│ suture merge │
│ feature-branch │
└────────┬─────────┘
│
▼
┌──────────────────┐ ┌────────────────────┐
│ Compute LCA │───▶│ PatchDag::lca() │
│ (merge base) │ │ Lowest Common │
│ │ │ Ancestor │
└────────┬─────────┘ └────────────────────┘
│
▼
┌──────────────────┐ ┌────────────────────┐
│ Per-file merge │───▶│ DriverRegistry │
│ │ │ lookup by extension │
└────────┬─────────┘ └────────────────────┘
│
┌────┴────┐
│ │
▼ ▼
┌───────┐ ┌───────┐
│Driver │ │Text │
│exists?│ │merge │
│ │ │fallback│
└───┬───┘ └───┬───┘
│ │
▼ ▼
┌──────────────────┐
│ Three-way merge │
│ driver.merge() │
│ or line-based │
└────────┬─────────┘
│
┌────┴────┐
│ │
▼ ▼
┌───────┐ ┌───────┐
│Clean │ │Conflict│
│Merge │ │Markers │
└───────┘ └───────┘
Deployment Topologies
Standalone (CLI Only)
┌──────────────────────────────┐
│ User Machine │
│ ┌────────┐ ┌────────────┐ │
│ │suture │──│ .suture/ │ │
│ │cli │ │ objects/ │ │
│ │ │ │ metadata.db│ │
│ └────────┘ └────────────┘ │
└──────────────────────────────┘
No server required. All operations are local. 17 semantic drivers available.
Hub + Clients
┌──────────────┐ HTTP/gRPC ┌──────────────────┐
│ Client A │◀──────────────────▶│ suture-hub │
│ (CLI/TUI) │ │ SQLite + Ed25519 │
└──────────────┘ │ auth, webhooks │
└────────┬─────────┘
┌──────────────┐ HTTP/gRPC │
│ Client B │◀────────────────────────────┘
│ (CLI/TUI) │
└──────────────┘
Optional: suture-s3 for blob storage (replaces SQLite blobs)
Optional: suture-raft for HA clustering (3+ nodes)
Full Platform (SaaS)
┌──────────────┐ HTTPS ┌──────────────────┐
│ Client │◀──────────────────▶│ suture-platform │
│ (Web UI / │ │ Axum + SQLite │
│ REST API) │ │ JWT auth, Stripe │
└──────────────┘ │ billing, orgs │
└────────┬─────────┘
┌──────────────┐ HTTPS │
│ VS Code / │◀───────────────────────────┘
│ JetBrains / │
│ Neovim │
└──────────────┘
Billing tiers: Free (100 merges/mo), Pro ($9/mo), Enterprise ($29/mo)
Analytics available on Pro+
WASM plugin uploads on Enterprise
Core Components
suture-common
Path: crates/suture-common/ Tests: 8
Foundation types shared across all crates. Zero dependencies.
HashPatchIdHash — identifies patches by contentBranchNameRepoPath.., absolute paths, null bytes)Key operations:
Hash::from_data(data)— compute BLAKE3 hashHash::from_hex(str)/to_hex()— hex encoding/decodingHash::ZERO— sentinel value (all zeros)
suture-core
Path: crates/suture-core/ Tests: 298 Depends on: suture-common
The core engine coordinating four subsystems:
src/cas/src/dag/graph.rsRepository::open().src/metadata/src/repository/repo_impl.rsAdditional capabilities:
- Audit Log (
src/audit.rs): Append-only JSONL with BLAKE3 hash chaining. Each entry contains hash of previous entry. Optional Ed25519 signatures. - Signing (
src/signing/): Ed25519 key generation, signing, verification for push operations. - File-type Detection: 14 file types with
auto_detect_repo_type(). - Semantic Diff Formatter: File-type-aware diff output with driver labels.
- Supply Chain Integrity: Shannon entropy analysis, 13 risk indicators, XZ-style attack detection.
- Conflict Classification: Categorize merge conflicts by type.
- Stash/RM/MV/Notes/GC/fsck/Squash/Patch composition: Full VCS lifecycle.
suture-driver
Path: crates/suture-driver/ Tests: 8 Depends on: suture-core, suture-common
Defines the SutureDriver trait and driver registry:
pub trait SutureDriver: Send + Sync {
fn name(&self) -> &str;
fn supported_extensions(&self) -> &[&str];
fn diff(&self, base: Option<&str>, new: &str) -> Result<Vec<SemanticChange>, DriverError>;
fn format_diff(&self, base: Option<&str>, new: &str) -> Result<String, DriverError>;
fn merge(&self, base: &str, ours: &str, theirs: &str) -> Result<Option<String>, DriverError>;
fn diff_raw(&self, base: &[u8], new: &[u8]) -> Result<Vec<SemanticChange>, DriverError> { ... }
fn merge_raw(&self, base: &[u8], ours: &[u8], theirs: &[u8]) -> Result<Option<Vec<u8>>, DriverError> { ... }
}
diff()/diff_raw()produceSemanticChangeentries (Added,Removed,Modified,Moved)merge()/merge_raw()returnSome(merged)for clean merges,Nonefor conflictsformat_diff()produces human-readable diff output
DriverRegistry (src/registry.rs): Dispatches to the correct driver by file extension. The CLI builds a builtin_registry() registering all 17 drivers.
SemanticChange types:
pub enum SemanticChange {
Added { path: String, value: String },
Removed { path: String, old_value: String },
Modified { path: String, old_value: String, new_value: String },
Moved { old_path: String, new_path: String, value: String },
}
Builtin Drivers (17)
suture-driver-json.jsonsuture-driver-yaml.yaml, .ymlsuture-driver-toml.tomlsuture-driver-csv.csvsuture-driver-xml.xmlsuture-driver-markdown.md, .markdown, .mdown, .mkdsuture-driver-sql.sqlsuture-driver-docx.docxsuture-driver-xlsx.xlsxsuture-driver-pptx.pptxsuture-driver-otio.otiosuture-driver-pdf.pdfsuture-driver-image.png, .jpg, .jpeg, .gif, .bmp, .webp, .tiff, .ico, .avifsuture-driver-html.html, .htmsuture-driver-svg.svgsuture-driver-feed.rss, .atomsuture-driver-ical.ics, .ifbSupporting crate: suture-ooxml — shared OOXML infrastructure (ZIP parsing, part navigation, per-part relationship resolution).
suture-hub
Path: crates/suture-hub/ Tests: 61 (with features) Depends on: suture-common, suture-core, suture-protocol
Central server for hosting Suture repositories.
Transport layers:
HTTP Routes (key endpoints):
/healthz/handshake/v2/handshake/v2/push/v2/pull/repos/repos/repo/{repo_id}/auth/register/auth/login/auth/token/auth/verify/search/lfs/batch/webhooks/replication/peersgRPC Service (SutureHub):
Defined in crates/suture-hub/proto/suture.proto. 14 RPCs:
Handshake, ListRepos, GetRepoInfo, CreateRepo, DeleteRepo, ListBranches, CreateBranch, DeleteBranch, ListPatches, GetBlob, Push, Pull, GetTree, Search
Features:
- Auth: Ed25519 key-based authentication
- Webhooks: Event-driven notifications (push/branch events) with HMAC-SHA256 signing
- S3 Backend (feature
s3-backend): Blob storage viasuture-s3(AWS SigV4, MinIO compatible) - Raft Clustering (feature
raft-cluster): Consensus viasuture-raftfor HA deployments - Mirrors: Repository mirroring support
- Pagination: Cursor-based pagination for large result sets
- LFS: Large file storage via batch API
suture-platform
Path: crates/suture-platform/ Depends on: suture-driver (all 17), suture-wasm-plugin
SaaS platform with web UI, authentication, billing, organizations, and merge API.
Architecture:
- Axum web server with tower-http tracing
- SQLite (WAL mode) via
PlatformDbwithMutex<Connection> - JWT authentication (HS256, 7-day expiry, revocation support)
- Rate limiting: In-memory sliding window per user/IP, tier-based limits
- CORS: Permissive (configurable for production)
- Stripe: Checkout sessions, portal, webhook handling with HMAC-SHA256 verification
- OAuth: Google and GitHub (CSRF state tokens, one-time use)
- WASM plugins: Runtime plugin loading and merge execution
See docs/api-reference.md for full endpoint documentation.
suture-raft
Path: crates/suture-raft/ Tests: 30
Raft consensus protocol implementation for hub clustering.
Key components:
RaftNodeRaftTcpTransportRaftRuntimeSqliteRaftLogpersist feature)HubCommandProtocol details:
- Randomized election timeouts (Raft paper section 5.2) to prevent split-vote livelock
- Leader election, log replication, commit
- Multi-node 3-cluster integration test over real TCP
- 3 Raft integration tests + 12 hub-Raft tests
suture-vfs
Path: crates/suture-vfs/ Tests: 28 (2 ignored — require root)
Exposes a Suture repository as a filesystem.
fuse3 + TokioIntegration tests (require root, #[ignore]d): mount read/write/modify/delete/stat, WebDAV serve.
suture-wasm-plugin
Path: crates/suture-wasm-plugin/
WASM plugin runtime for loading custom merge drivers at runtime.
ABI:
- Version 1 ABI with version checking
PluginManager: Load, validate, list, and execute WASM pluginsvalidate_plugin(): Pre-load validation of Wasm modules- Plugin descriptor files (
.suture-plugin) for discovery discover_plugins(dir): Scan a directory for plugin descriptors
Integration with platform:
- Plugins loaded at startup from
plugins/directory - Enterprise-tier users can upload plugins via REST API
merge_with_plugin()endpoint dispatches to loaded WASM drivers
suture-cli
Path: crates/suture-cli/ Tests: 32
Command-line interface using clap with derive macros.
64 subcommands organized in src/cmd/ (one file per command).
Key commands:
init--type and --template support)addcommitmerge, --dry-run`)diff--integrity, --classification, --summary, --name-only)log--stat, --diff, --audit, --graph)push / pullstashrebasebisecttuisyncdoctor--fix auto-remediation)exportundoverifyblame-L)grepswitch / restorearchivels-remotetimelinereportgit importkey generatehook list/run/editSupporting modules:
driver_registry.rsDriverRegistry with all 17 builtin driversdisplay.rsfuzzy.rsremote_proto.rsref_utils.rsstyle.rssuture-tui
Path: crates/suture-tui/ Tests: 31
Terminal UI built with ratatui.
7 tabs:
Conflict resolver:
- Line-by-line hunk resolution (replaces binary ours/theirs-only choice)
1/2/3keys: take ours, theirs, or both per hunkj/kkeys: navigate between conflict hunksn/pkeys: next/previous conflict fileakey: accept all remaining with last used resolution- Three-panel layout: file list, hunk detail, key bindings footer
suture-lsp
Path: crates/suture-lsp/ Tests: 11
Language Server Protocol implementation via tower-lsp.
Capabilities:
- Hover information for patches
- Diagnostics for merge conflicts
- Workspace-aware repository operations
- Activates for
.suturedirectories
suture-daemon
Path: crates/suture-daemon/ Tests: 33
Background daemon for continuous operations.
notify crate for working tree change detectionmemmap2-based SHM for inter-process statehumantime-based timestampsOther Crates
suture-protocolsuture-mergesuture-merge v0.2)suture-s3suture-ooxmlsuture-nodesuture-e2esuture-fuzzsuture-benchsuture-pyData Model
Repository
The Repository is the top-level object (suture-core/src/repository/). It coordinates:
Repository
├── BlobStore (CAS)
│ └── .suture/objects/{2-char-prefix}/{62-char-hash}
├── PatchDag (in-memory)
│ └── Nodes: patch_id → [parent_ids], branches: name → tip_patch_id
├── MetadataStore (SQLite)
│ └── .suture/metadata.db
│ ├── patches table (serialized JSON)
│ ├── branches table (name → tip)
│ └── config key-value pairs
├── Working Set
│ └── Currently checked-out files, staged changes
└── Audit Log
└── .suture/audit.jsonl (BLAKE3 hash chain)
Patch
The fundamental unit of change (suture-core/src/patch/types.rs):
idHash (BLAKE3)parent_idsVec<Hash>operation_typeOperationTypeCreate, Delete, Modify, Move, Merge, Batch, Identitytouch_setTouchSettarget_pathOption<String>payloadVec<u8>timestampi64authorStringmessageStringThe Batch operation type groups multiple FileChange entries into a single commit (standard CLI commit path).
FileChange
Within a Batch patch:
pathStringcontent_hashHashoperationFileOpAdd, Modify, DeleteTouch Set and Commutativity
Touch sets are the basis for commutativity detection. Two patches commute (can be applied in either order) if and only if their touch sets are disjoint. This enables conflict detection without full content comparison.
Branch
A branch maps a BranchName to a tip PatchId in the DAG.
Storage Layout
.suture/
├── objects/ # CAS blob storage (BLAKE3-indexed)
│ └── ab/ # 2-char hex prefix (256 buckets)
│ └── cdef... # Remaining 62 hex chars = blob filename
├── metadata.db # SQLite: patches, branches, config, refs
├── HEAD # Current branch reference
├── hooks/ # Executable hook scripts
├── keys/ # Ed25519 signing keys
├── config # Per-repo TOML configuration
├── audit.jsonl # Tamper-evident audit log
└── worktree # Marker file for worktree checkouts
Merge Algorithm
Suture implements three-way merge with format-aware conflict detection:
Step-by-Step
- Compute merge base:
PatchDag::lca()finds the Lowest Common Ancestor of the two branch tips - Reconstruct file trees: Apply patches from merge base to each branch tip, obtaining three versions per file:
base,ours,theirs - Per-file driver lookup: Check if a semantic driver exists for the file extension via
DriverRegistry - Driver present: Call
driver.merge(base, ours, theirs)(ormerge_raw()for binary formats) - No driver: Fall back to line-based text merge with conflict markers
- Conflict handling for binary formats (DOCX/XLSX/PPTX): Preserve "ours" version, generate
.suture/conflicts/report.md
Semantic Merge (Format-Aware)
When a driver is available:
- Parse all three versions (base, ours, theirs) using the format-specific driver
- Compute structural diff using key-path based changes (not line-based)
- Classify changes:
- Same: No change between base and either branch
- Ours-only: Changed in ours only → take ours
- Theirs-only: Changed in theirs only → take theirs
- Both-changed-same: Both changed identically → take either (no conflict)
- Both-changed-different: Both changed differently → conflict
- Auto-resolve: Take changed version for ours-only and theirs-only
- Detect conflicts: Both-changed-different entries produce conflicts
- Return:
Some(merged_content)for clean merge,Nonefor conflicts
Merge Strategies
-s semantic (default)-s ours-s theirs-s manualDry Run
--dry-run previews the merge result without modifying the working tree. OOXML conflict handling is noted in the output.
API Reference
Platform REST API
/healthz//static/{*path}/auth/register/auth/login/auth/me/auth/logout/auth/oauth/start/auth/google/callback/auth/github/callback/api/merge/api/drivers/api/usage/api/analytics/api/orgs/api/orgs/api/orgs/{org_id}/invite/api/orgs/{org_id}/members/api/orgs/{org_id}/members/{user_id}/role/api/orgs/{org_id}/members/{user_id}/api/invitations/api/invitations/{invite_id}/accept/api/plugins/api/plugins/upload/api/plugins/merge/billing/checkout/billing/portal/billing/subscription/billing/webhook/admin/usersHub REST API
/healthz/handshake/v2/handshake/v2/push/v2/pull/repos/repos/repo/{repo_id}/repo/{repo_id}/auth/register/auth/login/auth/token/auth/verify/search/activity/lfs/batch/webhooks/replication/peersgRPC Service (Hub)
SutureHub service defined in crates/suture-hub/proto/suture.proto:
Handshake, ListRepos, GetRepoInfo, CreateRepo, DeleteRepo, ListBranches, CreateBranch, DeleteBranch, ListPatches, GetBlob, Push, Pull, GetTree, Search
Wire Protocol (suture-protocol)
Binary protocol with Zstd compression for CLI-to-Hub communication:
- V2 handshake with capability negotiation
- Delta encoding for efficient transfer
- Batch operations for push/pull
Security Model
Authentication
Platform (JWT):
- HS256 JWT with configurable secret (
SUTURE_JWT_SECRET) - 7-day session duration
- Token revocation via
revoked_tokenstable (checked on every authenticated request) - Revoked tokens auto-cleaned on verification
Hub (Ed25519):
- Ed25519 key-based authentication
- Token creation and verification endpoints
- Key generation via
suture key generate
OAuth (Google, GitHub):
- Authorization code flow with PKCE-style state tokens
- CSRF protection: UUID v4 state stored in
oauth_statestable with 10-minute expiry - State is one-time use (deleted after validation)
- Provider identity stored as
oauth:<provider_id>marker in password_hash field
Authorization
Tier-based rate limiting (per-minute sliding window):
Rate limit headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, Retry-After
Anonymous requests: 10 req/min per IP (uses X-Forwarded-For header)
Stripe Integration
- Webhook HMAC verification: Stripe-Signature header parsed for
t=<timestamp>,v1=<signature>. HMAC-SHA256 computed overtimestamp.payload. Timestamp must be within 300 seconds to prevent replay attacks. - Customer management: Auto-create Stripe customer on first checkout. Customer ID stored in accounts table.
- Subscription lifecycle: Handles
checkout.session.completed,customer.subscription.updated,customer.subscription.deleted,invoice.payment_failed - Grace period: 7-day grace period on payment failure before downgrade
- Price IDs: Configured via
STRIPE_PRICE_PROandSTRIPE_PRICE_ENTERPRISEenv vars
Self-Hosted (Hub)
- Ed25519 token-based auth
- Per-IP rate limiting
- Repository-level access control
- Webhook delivery with HMAC-SHA256 signing
- Branch protection rules
Input Validation
RepoPath::new()rejects.., absolute paths, null bytes (path traversal protection)BranchName::new()validates non-empty, no null bytes- Email validation: must contain
@, max 254 characters - Password: minimum 8 characters
- Org names: 2-39 alphanumeric characters (hyphens and underscores allowed)
- Blob size limits: 50MB max (
max_blob_size) - Page size limits: 10K max (
max_page_size)
Audit Trail
- Append-only JSONL with BLAKE3 hash chaining
- Optional Ed25519 signatures for non-repudiation
suture log --auditfor compliance reporting- Export formats: JSON, CSV, text
Error Handling
All crates use thiserror for error types:
RepoErrorCasErrorDagErrorDriverErrorMetaErrorMergeErrorOrgErrorDependency Graph
suture-common (no deps)
↑
suture-core → suture-common
↑
suture-driver → suture-core, suture-common
↑
suture-merge → suture-driver
suture-ooxml → suture-common
suture-driver-{docx,xlsx,pptx} → suture-driver, suture-ooxml
suture-driver-{json,yaml,toml,csv,xml,...} → suture-driver
suture-protocol → suture-common
suture-cli → suture-core, suture-common, suture-driver, suture-merge, all drivers
suture-hub → suture-common, suture-core, suture-protocol
suture-raft (standalone)
suture-s3 (standalone)
suture-platform → suture-driver (all 17), suture-wasm-plugin
suture-vfs → suture-core
suture-tui → suture-core
suture-lsp → suture-core
suture-daemon → suture-core
suture-wasm-plugin (standalone)
Total: 37 crates in workspace (2 excluded: suture-py, desktop-app).