Controlled Text Transfer (ctt) is a security-oriented CLI tool and Python library for transferring files across strict security boundaries—such as Cross-Domain Solutions (CDS), air-gapped networks, or restricted file transfer drops—that only permit plain text (.txt) files.
The ctt tool validates source files against explicit compliance policies, packages allowed files into a .txt-only transfer format (preserving full original paths and metadata in a signed-ready JSON manifest), and restores exact byte-identical originals at the destination.
Python 3.12.13 or newer is required. Earlier 3.12 patch releases omit upstream security fixes included in 3.12.13.
[ Source Directory ]
│
▼
1. preflight ──────► Audit source directory files against compliance policy (Read-only)
│
▼
2. prepare ────────► Package allowlisted files into .txt transfer directory or archive
│
[ CDS / Restricted Boundary Transfer ]
│
▼
3. verify ─────────► Check package directory/archive integrity, hashes & signatures
│
▼
4. restore ────────► Recreate original files byte-for-byte in destination directory
preflight: Inspects a directory to test files against policy rules without making changes.prepare: Converts approved files to.txttransfer copies, adds transport BOMs where configured, generatesctt-manifest.json.txt, and publishes a signed-ready bundle.verify: Validates manifest hashes, path boundaries, package structure, and optional digital signatures before unpacking.restore: Strips.txttransfer extensions and BOMs, recreating exact original files atomically at the destination.
Install official releases directly from PyPI (controlled-text-transfer):
pip install controlled-text-transferRun the core 4-step workflow using zero-configuration defaults:
# 1. Inspect source directory against policy (prints concise summary)
ctt preflight ./source_dir
# 2. Package allowlisted files into a transfer directory
ctt prepare ./source_dir ./transfer_dir
# 3. Verify transfer package directory (or archive)
ctt verify ./transfer_dir
# 4. Restore original files into a new destination directory
ctt restore ./transfer_dir ./restored_dirPath Roles & Optional Flags:
- Directory Arguments:
./source_diris the input folder containing files;./transfer_diris the output transfer folder (or archive path);./restored_diris the new output folder where original files are recreated.- Zero Required Flags:
cttworks out of the box with zero flags using built-in safe defaults (generic-text-v1profile, common text allowlists, SHA-256).--json(preflight): Optional flag to output complete machine-readable JSON details instead of a summary text report.--strict(prepare): Optional flag to fail closed and create nothing if any single candidate file is rejected.--policy PATH: Optional flag to load custom YAML rules (e.g.ctt.yaml) instead of built-in defaults.
For detailed quickstart guidance, see README-quickstart.md.
The ctt CLI cleanly separates end-user transfer operations from developer maintenance tools.
| Command | Purpose | Default Usage | Expressive Usage with Options |
|---|---|---|---|
preflight |
Produce a read-only policy compatibility report | ctt preflight ./source_dir |
ctt preflight ./source_dir --policy ctt.yaml --json |
prepare |
Package allowlisted files into .txt transfer format |
ctt prepare ./source_dir ./transfer_dir |
ctt prepare ./source_dir ./dist/pkg.zip --policy ctt.yaml --strict --json-report report.json --log-json |
verify |
Verify package integrity, manifest, and signatures | ctt verify ./transfer_dir |
ctt verify ./transfer.zip --require-signature --log-json |
restore |
Restore original byte-identical files | ctt restore ./transfer_dir ./restored_dir |
ctt restore ./transfer.zip ./restored_dir --dry-run --log-json |
| Command | Purpose | Default Usage | Expressive Usage with Options |
|---|---|---|---|
diff |
Compare transfer package against source directory | ctt diff ./transfer_dir ./source_dir |
ctt diff ./transfer.zip ./source_dir --policy ctt.yaml --json |
self-package |
Package CTT itself into a .txt-only self-bootstrapping bundle |
ctt self-package ./ctt-bootstrap.zip |
ctt self-package ./dist/ctt-bootstrap.tgz --source . --format tgz |
Here are common operational scenarios showing how to combine additional CLI options:
Evaluate a source directory before transfer:
# Summary stdout report using built-in policy defaults
ctt preflight ./source_dir
# Machine-readable JSON preflight report using custom policy
ctt preflight ./source_dir --policy ctt.yaml --json > preflight-audit.jsonEnforce strict validation (fail if any candidate file is rejected), save a preflight report, and output a JSON audit event to stderr:
# Package into a ZIP archive with strict policy enforcement and audit outputs
ctt prepare ./source_dir ./dist/transfer.zip \
--policy ctt.yaml \
--strict \
--json-report ./preflight.json \
--log-jsonVerify digital signatures and test restoration without writing to disk:
# Verify transfer package requiring an authenticated digital signature
ctt verify ./transfer.zip --require-signature --log-json
# Dry-run restoration to inspect files without writing output
ctt restore ./transfer.zip ./restored_dir --dry-run
# Perform real atomic restoration with JSON audit logging
ctt restore ./transfer.zip ./restored_dir --require-signature --log-jsonInspect changes between a transfer package and a live source directory:
ctt diff ./transfer_dir ./source_dir --policy ctt.yaml --jsonPackage the ctt application itself into a standalone .txt-only transfer bundle with embedded zero-dependency bootstrapper:
ctt self-package ./dist/ctt-bootstrap.zip --format zipThe embedded bootstrap requires Python 3.12.13 or newer. It bounds manifests, ZIP
input, expanded content, member count and size, compression ratios, and paths; it
rejects links, duplicate or encrypted members, and multiple manifests. Because the
zero-dependency bootstrap has no trusted signer integration, it rejects signed
packages. Use the installed ctt restore command to authenticate and restore them.
For complete CLI flag documentation, see the CLI option reference.
ctt uses an explicit policy-driven allowlist:
- Default Policy (No
--policyflag required): UTF-8 decoding, explicit extension/name allowlists, SHA-256 hashes, and a 10 MiB per-file limit. - Custom Policy (
--policy PATH): Load custom rules from a YAML file (e.g.ctt.yaml). - Ignored Files: Files matching
.cttignoreor failing allowlist criteria are safely omitted from transfer. generic-text-v1Profile: Enforces file count, aggregate size, path depth, character sets, line lengths, control character checks, and forbidden textual patterns.- Hash Support: SHA-256 (default), SHA-512 (
hash_algorithm: sha512), and optional BLAKE3 (uv sync --extra blake3).
See the policy reference and examples for full configuration details.
- Atomic Restoration: Destinations are staged and validated before publication to prevent partial writes.
- Traversal Prevention: Every manifest path is verified against a link-free package root.
- Digital Signatures: Detached signature hooks in
controlled_text_transfer.signingintegrate with host GPG/X.509 infrastructure.cttfails closed if a signature is missing when--require-signatureis specified. - Bounded Ingestion: Immutable receiver ceilings bound manifests, signatures, archives, expansion, members, paths, and individual files. Security-sensitive reads use stable descriptors and archive extraction streams observed bytes.
- Authenticated Signer Identity: Identity-bearing manifests require an exact
identity returned by the trusted verifier;
key_labelis informational only.
See the security hardening contract for exact binary-unit ceilings, compatibility rules, residual risks, and test evidence.
See SECURITY.md for detailed security guidance.
Developer workflow and project maintenance scripts are managed separately from user CLI commands:
# Environment setup
bash scripts/run.sh setup
uv sync --extra dev
# Run test suite and quality gates
bash scripts/run.sh check
uv run --extra dev pytest
# Generate test coverage and API documentation reports
bash scripts/run.sh report
# Clean generated build and test artifacts
bash scripts/run.sh clean --dry-run
bash scripts/run.sh cleanFor full details on repository scripts and release workflows, see Development scripts and AGENTS.md.
GitHub-specific automation enforces the same build, test, security, and release requirements documented for local development. It uses least-privilege permissions, immutable action references, and locked Python dependencies.
- Workflow definitions
dependabot.ymlmaintains pinned GitHub Actions dependencies.
Releases use PyPI Trusted Publishing through .github/workflows/release.yml. Configure a pending GitHub publisher for project controlled-text-transfer, owner dgomez407, repository ctt, workflow release.yml, and environment pypi. After quality gates pass, push a version tag such as v0.1.0 from a commit contained in main. The workflow verifies that the tagged commit belongs to origin/main, the tag, package version, README, and changelog agree; reruns the locked quality gates; validates the wheel and source distribution; and publishes them without a stored API token.