Skip to content

Compose Texo as a BatPak scale-out system - #11

Merged
heyoub merged 9 commits into
mainfrom
feat/batpak-scaleout-composition
Jul 15, 2026
Merged

Compose Texo as a BatPak scale-out system#11
heyoub merged 9 commits into
mainfrom
feat/batpak-scaleout-composition

Conversation

@heyoub

@heyoub heyoub commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

What changed

This composes Texo through the BatPak 0.10 family as a scale-out system instead of treating one physical store as the product boundary.

  • replaces hand-rolled operation assembly with a sealed hostbat module and real composition fingerprints
  • adds explicit multi-journal workspace topology with canonical and replica authority roles
  • implements identity-preserving exact forks plus resumable imported read models with durable cursor/anchor/batch evidence
  • adds bounded authenticated local and remote replica circuits using BatPak, syncbat, and netbat primitives
  • routes CLI, HTTP, SSE, MCP, VS Code, and snapshot tokens through journal identity
  • makes texo install --client all provision independent Codex, Claude, and Cursor read replicas and refresh them before MCP serves
  • composes BatPak backup envelopes and restore proofs into Texo backup verification
  • confines configured extractors through an isolated bvisor helper with no unconfined fallback
  • ships the bvisor helper and launcher in the ECS deployment path
  • records the full use/recompose/shim/reject decision ledger in docs/BATPAK-SCALEOUT-COMPOSITION.md

Why

BatPak's single-writer contract is per physical materialization, not a global scaling ceiling. Texo now uses the intended coordinate/CQRS model: canonical logs own ordered authority-bearing writes; independent stores materialize read models and serve concurrently. Replica lag, identity, anchors, and authority are explicit rather than inferred.

Neutral BatPak 0.10 gaps remain behind deletable typed shims. Upstream replacements are tracked in freebatteryfactory/batpak_DEPRECATED#227 through #233; no gap waters down the product path.

Security and failure behavior

  • remote plaintext circuits accept only concrete loopback/private IP endpoints
  • request secrets never cross the wire; keyed BLAKE3 tags cover the complete request
  • response sizes are bounded before decode
  • restart resumes from durable source cursor and anchor
  • wrong-journal snapshot tokens, source swaps, and anchor regressions fail closed
  • bvisor grants read-write only to a private staging directory, read-only runtime roots, denies network, and publishes output only with completion evidence
  • replica hosts reject persist/emit/control effects before handler dispatch

Validation

  • just verify: fmt, clippy -D warnings, cargo-deny, typos, hygiene, and full suite green
  • just test-invariants: projection laws 10/10, compile-fail 1/1, BatPak family spikes 7/7
  • all-target/all-feature check and helper-specific clippy green
  • literal frozen 0.9 store matrix: 7/7; six claim snapshots byte-identical; CRC corruption fails closed; four no-op reingests append zero; store lock preserved
  • two fresh appliance rehearsals pass end to end
  • Git/code intelligence snapshot + restore demo passes
  • release-mode local replica, remote replica, and three-client suite: 0.97s wall / 80,812 KiB max RSS after build
  • real bvisor workload completes; /tmp escape attempt creates no file and no completion marker

No paid model calls were made by the new tests. The immutable hackathon tag and main were not modified.

Greptile Summary

This PR turns Texo into a journal-aware BatPak scale-out system. The main changes are:

  • Canonical and replica journal topology with explicit authority roles.
  • Resumable local and remote read-model replication.
  • Journal-aware CLI, HTTP, SSE, MCP, VS Code, and install flows.
  • Sealed hostbat composition and BatPak backup evidence.
  • Isolated extractor execution through a shipped bvisor helper.

Confidence Score: 4/5

Backup recovery and unchanged replica refresh need fixes before merging.

Existing backups can be rejected after upgrading. Restore can silently discard saved workspace behavior and journal routes. A same-length source replacement can be reported as refreshed while stale data remains.

src/backup.rs and src/replication.rs

T-Rex T-Rex Logs

What T-Rex did

  • The reproduction confirmed that existing backups become unrestorable when the manifest lacks substrate_manifest, with verification rejecting the pre-v2 envelope and restore failing for a v1 backup.
  • A production backup and restore was executed for a workspace with custom settings and a named exact-fork replica; the named replica started successfully and seven events were verified during startup.
  • A focused test replaced the source at frontier 14 with a different history; the refresh closed with AnchorMismatch, indicating the refresh does not skip source validation.
  • The scaleout release run was captured by before/after logs showing a successful release (exit code 0) and seven per-test outcomes using the scaleout-focused harness.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
src/replication.rs Adds durable cursors, source identity checks, batching, retries, and local and remote replica refresh; the unchanged-source shortcut bypasses an identity check.
src/replica_net.rs Adds authenticated and bounded replica paging over private or loopback TCP endpoints.
src/topology.rs Adds normalized journal resolution, authority roles, unique store-path validation, and replica-cycle checks.
src/backup.rs Adds BatPak backup and restore evidence, but breaks old manifest compatibility and restores workspace defaults instead of saved settings.
src/bin/texo-bvisor-extractor.rs Adds the isolated Linux extractor helper with restricted filesystem access, denied networking, resource limits, and completion evidence.
src/install.rs Adds independent client replica provisioning, journal-aware adapter configuration, and refresh-before-serve behavior.
src/host/module.rs Moves operation composition into a sealed hostbat module with role-specific admission.
src/config.rs Expands workspace configuration to a normalized multi-journal topology.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
C[Canonical journal] -->|paged import| R1[Codex replica]
C -->|paged import| R2[Claude replica]
C -->|paged import| R3[Cursor replica]
C -->|exact fork| F[Identity-preserving fork]
R1 --> M1[MCP]
R2 --> M2[MCP]
R3 --> M3[MCP]
C --> B[Backup envelope]
B --> V[Offline verification]
V --> W[Restored workspace]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart LR
C[Canonical journal] -->|paged import| R1[Codex replica]
C -->|paged import| R2[Claude replica]
C -->|paged import| R3[Cursor replica]
C -->|exact fork| F[Identity-preserving fork]
R1 --> M1[MCP]
R2 --> M2[MCP]
R3 --> M3[MCP]
C --> B[Backup envelope]
B --> V[Offline verification]
V --> W[Restored workspace]
Loading
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
src/backup.rs:29
**Existing Backups Become Unrestorable**

Changing the only accepted manifest schema to `texo.backup.v2` leaves no reader or migration path for backups created as `texo.backup.v1`. After upgrading, verification rejects those existing backups before restore, so an operator cannot recover data from a previously valid Texo backup.

### Issue 2 of 3
src/backup.rs:374-381
**Restore Replaces Workspace Configuration**

This reconstructs the workspace with `WorkspaceEntry::for_id` instead of restoring the backed-up entry. A workspace with custom extraction, semantic settings, document globs, or named replica journals is restored with defaults, so later ingestion can produce different results and clients pinned to those journals fail to start.

### Issue 3 of 3
src/replication.rs:313-314
**Unchanged Frontier Skips Source Validation**

When a source store is replaced by different history with the same final sequence, this shortcut returns the old cursor before comparing its recorded event identity with the current source. The replica is then reported as refreshed and can serve stale history until the replacement source advances.

Reviews (1): Last reviewed commit: "fix(confinement): make runtime roots rea..." | Re-trigger Greptile

Greptile also left 3 inline comments on this PR.

Context used:

  • Context used - AGENTS.md (source)

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 55 files, which is 5 over the limit of 50.

To get a review, narrow the scope:
• coderabbit review --type committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1386302a-0738-467c-84d3-30283ad5b875

📥 Commits

Reviewing files that changed from the base of the PR and between fe0cc27 and b06eeaf.

⛔ Files ignored due to path filters (4)
  • Cargo.lock is excluded by !**/*.lock
  • tests/snapshots/golden_agent_context__agent_context_demo.snap is excluded by !**/*.snap
  • tests/snapshots/golden_ingest__ingest_demo.snap is excluded by !**/*.snap
  • tests/snapshots/golden_staleness__staleness_stale_onboarding.snap is excluded by !**/*.snap
📒 Files selected for processing (56)
  • ARCHITECTURE.md
  • Cargo.toml
  • INVARIANTS.md
  • README.md
  • SPEC.md
  • deploy/deploy.sh
  • deploy/env.example
  • docs/BATPAK-SCALEOUT-COMPOSITION.md
  • extensions/vscode/README.md
  • extensions/vscode/package.json
  • extensions/vscode/src/runner.ts
  • scripts/verify-old-store.sh
  • src/backup.rs
  • src/bin/texo-bvisor-extractor.rs
  • src/claims/evidence.rs
  • src/claims/temporal.rs
  • src/claims/workspace.rs
  • src/compat/batpak.rs
  • src/compat/bvisor.rs
  • src/compat/mod.rs
  • src/compat/netbat.rs
  • src/config.rs
  • src/doctor.rs
  • src/error.rs
  • src/events/payloads.rs
  • src/hooks.rs
  • src/host/fingerprint.rs
  • src/host/mod.rs
  • src/host/module.rs
  • src/install.rs
  • src/journal_store.rs
  • src/knowledge.rs
  • src/lib.rs
  • src/ops/agent.rs
  • src/ops/backend.rs
  • src/ops/env.rs
  • src/ops/handlers.rs
  • src/replica_net.rs
  • src/replication.rs
  • src/surfaces/bootstrap.rs
  • src/surfaces/cli/mod.rs
  • src/surfaces/http/routes.rs
  • src/surfaces/http/sse.rs
  • src/surfaces/mcp_stdio.rs
  • src/topology.rs
  • tests/agent_context.rs
  • tests/appliance_install.rs
  • tests/backup.rs
  • tests/helios_e2e.rs
  • tests/http_server.rs
  • tests/multi_client_scaleout.rs
  • tests/ops_kit.rs
  • tests/projection_laws.rs
  • tests/remote_replication.rs
  • tests/replication_scaleout.rs
  • tests/topology_scaleout.rs

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Review on demand using usage pricing
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/batpak-scaleout-composition

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@heyoub
heyoub marked this pull request as ready for review July 15, 2026 20:13

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b06eeafdc9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/surfaces/cli/mod.rs
Comment on lines +1025 to +1029
let socket =
crate::compat::netbat::private_socket_addr(&addr).map_err(|error| TexoError::Config {
detail: format!("replica listener {addr}: {error}"),
source: None,
})?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Allow the deployed HTTP bind address

When texo serve runs under the provided systemd deployment, TEXO_AGENT_ADDR defaults to 0.0.0.0:8787 in deploy/env.example, but this path validates the normal HTTP listener with private_socket_addr, which rejects wildcard/public endpoints as if they were plaintext replica clients. That makes the deployed service fail during startup before binding the listener; the private-address restriction should be applied only to replica netbat endpoints, not the public HTTP server bind address.

Useful? React with 👍 / 👎.

Comment on lines +63 to +65
Capability::Network {
policy: NetPolicy::DenyAll,
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Do not deny network to the default LLM extractor

When bootstrap sees TEXO_LLM_API_KEY, it writes texo extract as the configured extractor, and the deploy env configures hosted DashScope models; that extractor needs outbound network on cache misses. Running it under this helper with NetPolicy::DenyAll makes normal session/ingest extraction fail for uncached documents even though the model key is configured, so the confinement policy needs a path for the intended hosted extractor or bootstrap should not select it.

Useful? React with 👍 / 👎.

Comment thread src/topology.rs
Comment on lines +20 to +24
if value.is_empty()
|| value.len() > 128
|| !value
.bytes()
.all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'_' | b'.'))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject dot-only journal ids before using them as paths

A topology can currently declare a journal id of . or .. because dots are allowed and there is no special-case rejection. Replica cursor/evidence paths later join the journal id as a directory component, so texo replica follow .. can write outside the intended .texo/replication/<workspace>/<journal>/ subtree. The validator should reject ./.. (and probably dot-only path components) as the doc comment promises for path-like ids.

Useful? React with 👍 / 👎.

@heyoub
heyoub merged commit cc0b7ba into main Jul 15, 2026
4 checks passed
Comment thread src/backup.rs
@@ -25,7 +29,7 @@ use crate::config::WorkspaceConfig;
use crate::error::TexoError;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Existing Backups Become Unrestorable

Changing the only accepted manifest schema to texo.backup.v2 leaves no reader or migration path for backups created as texo.backup.v1. After upgrading, verification rejects those existing backups before restore, so an operator cannot recover data from a previously valid Texo backup.

Context Used: AGENTS.md (source)

Artifacts

Repro: executable harness that generates a backup, converts it to the pre-v2 manifest shape, and exercises verification and restore

  • Contains supporting evidence from the run (text/x-shellscript; charset=utf-8).

Repro: runtime output showing v1 verification and restore rejection

  • Keeps the command output available without making the summary code-heavy.

Repro: focused backup suite output confirming the current v2 path succeeds

  • Keeps the command output available without making the summary code-heavy.

View artifacts

T-Rex Ran code and verified through T-Rex

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/backup.rs
Line: 29

Comment:
**Existing Backups Become Unrestorable**

Changing the only accepted manifest schema to `texo.backup.v2` leaves no reader or migration path for backups created as `texo.backup.v1`. After upgrading, verification rejects those existing backups before restore, so an operator cannot recover data from a previously valid Texo backup.

**Context Used:** AGENTS.md ([source](https://app.greptile.com/heyoub/github/freebatteryfactory/texo/-/custom-context?memory=7a12442e-73af-4ed1-b813-3fb795a7bbff))

How can I resolve this? If you propose a fix, please make it concise.

Comment thread src/backup.rs
Comment on lines 374 to +381
.get(&manifest.workspace_id)
.cloned()
.ok_or_else(|| backup_error("backup config does not contain its workspace"))?;
workspace.store_path = crate::config::WorkspaceEntry::for_id(&manifest.workspace_id).store_path;
let restore_store_path = crate::config::WorkspaceEntry::for_id(&manifest.workspace_id)
.primary()
.map_err(|error| backup_error(error.to_string()))?
.store_path;
workspace

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Restore Replaces Workspace Configuration

This reconstructs the workspace with WorkspaceEntry::for_id instead of restoring the backed-up entry. A workspace with custom extraction, semantic settings, document globs, or named replica journals is restored with defaults, so later ingestion can produce different results and clients pinned to those journals fail to start.

Context Used: AGENTS.md (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/backup.rs
Line: 374-381

Comment:
**Restore Replaces Workspace Configuration**

This reconstructs the workspace with `WorkspaceEntry::for_id` instead of restoring the backed-up entry. A workspace with custom extraction, semantic settings, document globs, or named replica journals is restored with defaults, so later ingestion can produce different results and clients pinned to those journals fail to start.

**Context Used:** AGENTS.md ([source](https://app.greptile.com/heyoub/github/freebatteryfactory/texo/-/custom-context?memory=7a12442e-73af-4ed1-b813-3fb795a7bbff))

How can I resolve this? If you propose a fix, please make it concise.

Comment thread src/replication.rs
Comment on lines +313 to +314
if let Some(cursor) = unchanged_local_cursor(previous, source_ceiling) {
return Ok(unchanged_report(root, circuit, cursor));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Unchanged Frontier Skips Source Validation

When a source store is replaced by different history with the same final sequence, this shortcut returns the old cursor before comparing its recorded event identity with the current source. The replica is then reported as refreshed and can serve stale history until the replacement source advances.

Context Used: AGENTS.md (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/replication.rs
Line: 313-314

Comment:
**Unchanged Frontier Skips Source Validation**

When a source store is replaced by different history with the same final sequence, this shortcut returns the old cursor before comparing its recorded event identity with the current source. The replica is then reported as refreshed and can serve stale history until the replacement source advances.

**Context Used:** AGENTS.md ([source](https://app.greptile.com/heyoub/github/freebatteryfactory/texo/-/custom-context?memory=7a12442e-73af-4ed1-b813-3fb795a7bbff))

How can I resolve this? If you propose a fix, please make it concise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant