Controlled Text Transfer (ctt) is a file transformation and integrity utility. It is not a Cross
Domain Solution, malware scanner, content disarm system, or authorization
boundary. Use it only inside an approved transfer procedure and keep the CDS,
anti-malware, and human review controls in place.
- Treat the source directory, transfer medium, archive, and destination as separate trust zones.
- The source directory is read-only from the tool's perspective.
- A manifest checksum detects accidental or unauthorized modification but does not prove who created the package.
- Authenticity requires a separately managed signature and trusted key path.
- Review the YAML policy and
.cttignorefile before preparation. - Inspect the prepare report and skipped-file list.
- Run
verifyafter the package crosses the transfer boundary. - If signatures are used, verify them with approved GPG, X.509, HSM, or enterprise tooling before restoration.
- Restore into a new, empty destination directory.
- Run destination malware scanning and application-specific tests.
The signing API accepts detached signatures through a small protocol or an
external command adapter. Private keys, passphrases, PINs, and trust stores
must remain in approved key-management tooling. Do not put them in command
arguments, environment variables, YAML, manifests, logs, or source files.
External commands are passed as argument vectors with shell=False; do not
replace this with shell-string execution. Secret-bearing flags are rejected in
both --flag value and --flag=value forms. CTT drains stdout and stderr
concurrently, retains no more than 256 KiB from either stream, and terminates a
command as soon as either stream exceeds that ceiling. Signing reports a
controlled failure; verification fails closed.
The tool uses an explicit text allowlist, UTF-8 decoding, file-size limits, path and filename limits, content-policy checks, path-root checks, and checksum verification. ZIP/TAR ingestion rejects traversal, links, special files, duplicate members, unexpected layouts, and excessive expansion. Archives must still be scanned by CDS and malware controls; renaming a file or adding a BOM is not a security bypass.
Directory ingestion rejects symbolic links and Windows junctions at the
package root, metadata sidecars, payload root, and every payload descendant.
Manifests may select only SHA-256, SHA-512, or optional BLAKE3 and may restore
only ordinary permission bits from 0o000 through 0o777. Oversized source
files are rejected before their content is read.
Preparation captures accepted bytes once, writes into a sibling staging directory, self-verifies, and publishes by rename. This avoids ordinary partial packages; filesystem or host failure semantics still apply. Signature verifiers must be supplied through trusted operator configuration and must never be selected from transferred data.
Security-sensitive reads use stable descriptors after rejecting links, junctions, and non-regular files and comparing pre-open, opened, and post-open identities. Archive ingestion streams observed bytes into staging under immutable receiver ceilings that transferred data cannot raise. Exact binary units, limits, compatibility effects, and test evidence are in the security hardening contract.
Identity-bearing manifests require an exact identity returned by the trusted
verifier. key_label is informational only; integrity success does not prove
authenticity. Legacy identity-free manifests may temporarily use boolean
verification. --allow-unverified-signature remains an explicit residual risk
and must not be treated as authorization to restore.
Restoration uses a sibling staging directory, validates reconstructed and persisted bytes, applies validated modes, and publishes by rename. A failed restore removes staging and leaves the requested destination absent.
scripts/clean.py deletes only allowlisted generated paths contained within
the resolved repository root. Discovery does not follow symbolic links or
Windows junctions/reparse points, preventing repository content from
redirecting recursive deletion outside the checkout. Review --dry-run
output before cleanup. Removing .venv is opt-in and is refused when it
contains the active Python interpreter. The shared uv cache is outside this
capability's scope.
Report output may contain absolute source paths, local account or platform
details, dependency versions, and diagnostic text. Treat reports/ as
developer-local data: review and redact it before sharing, and never place
secrets in test, lint, type-check, or security-scan output.
The report generator rejects repository-local output outside the ignored
top-level reports/ directory. It also rejects an existing output tree that
contains symbolic links or Windows junctions, preventing linked paths from
redirecting writes outside the selected directory.
GitHub Actions use explicit least-privilege permissions and full commit SHA
references for third-party actions. Dependabot proposes updates to those pins;
review the referenced upstream release before merging an update. CI installs
the committed uv.lock state with uv sync --frozen --extra dev.
CI runs for pushes to dev and main and for pull requests targeting main.
The separate event concurrency groups intentionally preserve both branch-tip
and prospective-merge validation. PyPI publication remains isolated in its own
job with environment-scoped OIDC permission, and a release tag is rejected
unless its checked-out commit belongs to origin/main.
The zero-dependency bootstrap applies bounded, stable reads to package directories and ZIP archives. It rejects links, duplicate or encrypted ZIP members, excessive expansion or compression, unsafe paths, multiple manifests, and signed packages it cannot authenticate. Signed packages must be restored with an installed, trusted CTT verifier.
Python 3.12 support begins at 3.12.13, the current upstream security release. CI pins that minimum patch explicitly and also tests Python 3.14. Operators must update when a later security patch is released; Python 3.12 support will be reassessed by October 2027 and removed no later than its October 2028 upstream end of life.
Do not include secrets or sensitive file contents in bug reports. Report code execution, path traversal, unexpected overwrite, signature bypass, or secret disclosure issues privately to the project maintainers before public disclosure.