Move data between incompatible systems. Prove the route before writing.
Keep plaintext at the endpoints. Stop cleanly when the evidence is not good enough.
LOCAL-FIRST · FAIL-CLOSED AUTO · EXPLICIT WRITE OUTCOMES
Ship safe imports and integrations without rebuilding the dangerous edge cases.
Trust Center and two-minute own-data trial
python -m pip install "https://github.com/IamAngusU/polymorph/releases/download/v0.4.0a11/polymorph_bridge-0.4.0a11-py3-none-any.whl"
polymorph trial C:\path\to\customers.xlsx --openNo account, no cloud, no destination credentials and no write authority.
Measured baselines, not universal promises. Click a metric for context.
Open source under AGPL-3.0-only · alternative commercial licensing available.
Polymorph is a local-first, policy-driven data bridge for recurring imports and integrations that are too important for a hopeful script and too awkward for a large integration platform.
System A calls a field customer_no. System B expects account_id. Then someone moves a spreadsheet column, renames a header, adds a formula, changes a foreign-key contract or sends the same report with a different layout.
The traditional solution is often some variation of "map it once and hope nobody touches anything".
Polymorph has trust issues instead.
Its core reliability rule is simple:
Uncertainty must reduce automation, never increase guessing.
A filename is evidence. A successful parser call is evidence. A model score is evidence. A recipe that worked yesterday is evidence.
Yesterday was also a different day.
Automatic promotion requires independently strong deterministic evidence, an immutable validated plan and a complete no-write preflight. If the system cannot prove a route inside its declared operating domain, it asks for review instead of manufacturing confidence.
Stable protocol and persisted-state namespaces are intentionally decoupled from the product name so a later rename does not invalidate encrypted envelopes, delivery state or recipe history. Branding is allowed to have a midlife crisis. Persisted cryptographic state is not.
| I want to... | Start with | What happens |
|---|---|---|
| Try Polymorph locally | python scripts/dev.py demo --open |
Builds a self-contained synthetic HTML walkthrough. No account, network, model or destination write. |
| Embed Polymorph | docs/PRODUCT_API.md |
Use the explicit move(...).prepare() then execute() API, product events and structured outcomes. |
| Evaluate the security model | SECURITY.md |
Review trust boundaries, honest limitations, failure semantics and hardening status before deployment. |
| Prepare a CapabilityFabric inventory | docs/CAPABILITY_FABRIC.md |
Build a digest-bound, secret-free account metadata plan; credentials never enter Polymorph. |
Connector authors can start at docs/CONNECTORS.md. Installed third-party
connector code is never discovered or imported unless the host explicitly opts in.
Another AI column mapper is not much of a product. It is a feature, and several vendors already have one.
Polymorph is intended to be the trust layer between systems that were never designed to agree with each other.
The useful version does four things well:
- Understand the source conservatively. Inspect bytes, structure, schema, relationships and policy before trusting labels.
- Prove the route before writing. Mapping, transforms and foreign-key resolution are validated against one exact plan.
- Keep plaintext at the endpoints. The relay receives ciphertext and routing metadata, not the payload values it transports.
- Fail in boring ways. Ambiguous mappings require review. Unknown write outcomes remain unknown. Retries do not become optimism with a loop around them.
Initial users are developers and technical operators with recurring imports where a wrong identifier, amount, credential or foreign key is expensive:
- agencies moving customer exports into several client systems
- small operations teams importing Excel, CSV and API data into an ERP or database
- security-conscious teams that cannot send payloads to a hosted mapping service
- software vendors that need a self-hosted ingestion edge for customer data
- regulated teams that need a reviewable plan and an honest record of uncertain writes
Polymorph deliberately separates useful signals from signals that may authorize automation.
| Signal | Useful? | Can authorize a write by itself? |
|---|---|---|
| File extension or display name | Yes | No |
| Successful parser call | Yes | No |
| Magika classification | Yes | No |
| Old recipe | Yes | No |
| Embedding similarity | Yes | No |
| Cross-encoder score | Yes | No |
| Current schema, policy and deterministic contract evidence | Yes | Yes, when all gates pass |
Useful is not authority.
A model may improve candidate ordering. It does not get a pen.
If a high-confidence model result disagrees with the independently strongest deterministic target, the mapping is review-required. A larger number after the decimal point is not a new trust boundary.
- Content-first file inspection. Parser selection does not trust the extension or display name.
- Opened-handle identity binding for CSV, JSON and Excel closes ordinary path-swap gaps between inspection and parsing.
- ZIP and OOXML central-directory checks reject traversal, symlinks, duplicate or encrypted members, suspicious expansion, oversized members, macros and external workbook links or data connections before a workbook parser is opened.
- GZIP members are validated with bounded streaming and member-count limits.
- JSON and XML have explicit parse, depth, item, element and per-tag attribute budgets before expensive parser work.
- Optional local Magika evidence can challenge deterministic detection. A strong conflict blocks automatic parser selection rather than starting a confidence popularity contest.
- A fail-closed parser-worker foundation can inspect an exact-byte snapshot. Non-setid Linux Bubblewrap 0.12.0 or newer is the strict backend. On Windows, the worker enters a Job Object for CPU, memory and process-tree containment. That is resource containment, not filesystem or network sandboxing.
- Excel parsing requires XML hardening through
defusedxml, then performs layout discovery for title rows, moved columns, repeated headers and fixed-width identifiers such as000042. - Spreadsheet formulas are detected separately. Cached formula results have unproven freshness and therefore block automatic recipe promotion.
- CSV dialect selection uses a deterministic candidate ensemble and may include CleverCSV. A close tie is rejected instead of guessed. Revolutionary, apparently.
- Deterministic schema matching is the automatic authority.
- Matching uses names, aliases, declared types, nullability, sensitivity, field roles and verified relationship evidence.
- Optional local embeddings and a multilingual cross-encoder reranker may improve candidate order, but neither may independently authorize a mapping.
- Target collisions, directional type problems, sensitivity conflicts and weak relationship proof demote or block automation even when a score looks impressive.
- Versioned recipes store reusable memory, not permission. Every reuse is rebound to current exact schemas, receives a new plan digest, is validated again and must pass preflight again.
- Repeated rejected recipe runs reduce trust and suspend automatic reuse instead of making the matcher increasingly creative.
preparecombines content inspection, mapping, recipe reuse and a complete no-write preflight.- Full-scan preflight exercises source transforms and optional read-only foreign-key resolution without destination writes.
- Sampled scans are diagnostic only. They cannot auto-promote a recipe.
--max-input-recordsis a hard per-run blast-radius budget. The first record above the limit blocks readiness instead of silently approving a source that grew by two orders of magnitude over lunch.
- Full-record blind transport uses X25519, HKDF-SHA256 and ChaCha20-Poly1305.
- Source records are authenticated with Ed25519 identities bound to tenant and connector scope, including finite rotation drain and hard revocation.
- Destination recipient keys come from separately pinned Ed25519 destination identities, not from control-plane claims alone.
- Signed route-bound recipient certificates form a monotonic rotation chain. An optional local checkpoint rejects stale generations and competing forks.
- A durable source outbox persists the exact signed ciphertext used for the first send. Lost acknowledgement? Retry those exact bytes. Resealing the same logical record produces different authenticated ciphertext, because cryptography is not obligated to cooperate with a convenient retry implementation.
- The relay queue stores ciphertext plus required routing metadata, uses fenced leases and keeps a durable idempotency ledger.
- Destination delivery distinguishes proven
NOT_COMMITTEDwrites fromUNKNOWNoutcomes. Unknown does not become retry-safe because an exception looked apologetic. - Capability-gated atomic destination batches preserve per-record authentication, ledger, audit and replay evidence while using one transaction for supported SQLite and PostgreSQL writes.
- Every batch is bounded by record count and sealed-wire size.
- The destination runtime is pinned to one exact plan and target contract, then rechecks field set, required values, nullability and types before crossing the connector write boundary.
- CSV exports reject spreadsheet formula-like values by default.
- HTTP redirects never count as a committed write.
- Ed25519-signed capabilities scope privileged operations.
- Metadata-only audit events form a SHA-256 hash chain and may additionally be signed.
- Reason-code explanations expose why something was blocked or demoted.
- Recipe-health summaries make repeated rejection visible.
- Payload-free operational events carry run and correlation IDs for the currently instrumented workflow.
- Credential references and encrypted destination recipient key files avoid pretending raw connection strings are a secret-management strategy.
schema + policy + exact plan
|
v
+---------------+
| control plane |
+---------------+
|
plan digest
|
source trust | destination trust
boundary | boundary
| | |
v | v
+--------------+ | +------------------+
| source agent | | | destination agent|
+--------------+ | +------------------+
| plaintext | ^ plaintext
| local transforms| | FK lookup
v | |
seal to destination public key |
| |
v |
+--------------------------------------------------+
| ciphertext-only relay / data plane |
| route metadata, leases, digests, no private key |
+--------------------------------------------------+
The relay still sees metadata required to route a record, including tenant, connector IDs, record and transfer IDs, plan digest and field IDs. Payload confidentiality is not traffic-analysis resistance. Source and destination endpoints necessarily see plaintext at their respective trust boundaries.
The destination identity public key must reach the source through an operator-controlled channel independent of the control plane. The control plane may distribute signed recipient-key certificates, but it cannot replace their tenant, destination, key, validity or rotation metadata.
See recipient key authenticity and rotation.
Python 3.11 through 3.14 is exercised in CI.
Clone the repository or use GitHub's Code > Download ZIP and extract it. Polymorph uses no Git submodules or Git LFS assets. The shortest model-free local setup is the same on either path:
git clone https://github.com/IamAngusU/polymorph.git
cd polymorph
python scripts/bootstrap.py --skip-checks
python scripts/dev.py doctor
python scripts/dev.py connectors
python scripts/dev.py demo --open
python examples.pyWhen using the ZIP, start with cd in the extracted polymorph-main directory and run the final
three commands. The bootstrap creates .venv, installs the local checkout and ends with doctor;
manual virtual-environment activation is not required. Run python scripts/bootstrap.py without
--skip-checks when you also want the complete development test suite.
Optional research models are not required for the core:
python scripts/bootstrap.py --skip-modelspython examples.py runs a tiny deterministic mapping example without models or external services.
See Development setup for the deliberately local data layout.
The convenience API remains deliberately two-phase. Creating and preparing a session never writes:
from polymorph import ConnectorSpec, move
run = move(
source="./incoming/customers.csv",
destination=ConnectorSpec.destination(
"database",
url="sqlite:///application.sqlite",
table="customers",
),
max_input_records=10_000,
)
run.on("review_required", review_ui.open)
run.on("progress", progress_view.update)
prepared = run.prepare()
if prepared.ready:
outcome = run.execute()execute() is a same-process local convenience path, not the ciphertext-only relay. It currently
requires a content-inspected immutable file source and refuses secret or opaque forwarding. Database,
HTTP and custom sources can still be inspected and prepared; use the separately authorized secure
agent workflow when source snapshot identity or endpoint separation matters. Destinations are always
explicit. Polymorph never guesses a database table or remote resource from a URL.
See Embedding and product events for review handling, event localization and every structured outcome.
Inspect the destination schema, then let prepare inspect the source, find or build a mapping plan and run the complete no-write preflight:
polymorph inspect db 'sqlite:///target.sqlite' --table orders -o target.schema.json
polymorph prepare ./incoming-file target.schema.json --output-plan orders.plan.json --remember \
--max-input-records 5000A plan is written only when the route is promotable.
If evidence is insufficient, prepare exits as review-required instead of producing a confident-looking JSON file and leaving future-you to discover what it meant.
For a foreign-key route, add a read-only resolver so preflight can prove the natural-key lookup before promotion:
polymorph prepare ./orders-upload target.schema.json \
--resolver-db-url 'sqlite:///target.sqlite' \
--resolver-db-table ordersWhen you want each stage separately:
polymorph inspect auto ./upload.bin -o source.schema.json
polymorph map source.schema.json target.schema.json
polymorph plan create source.schema.json target.schema.json -o route.plan.json
polymorph plan validate route.plan.json source.schema.json target.schema.json
polymorph preflight ./upload.bin target.schema.json route.plan.jsonReview-level decisions are not inserted into a plan by default. --allow-review exists for an explicit operator decision, not as a way to ask the matcher to please stop being difficult.
The core is fully functional without a model.
The optional CPU descriptor encoder is local-only at runtime, pinned to an upstream revision and hash-verified during installation. The native SentencePiece tokenizer avoids loading the much larger JSON vocabulary representation.
pip install -e ".[semantic]"
polymorph model install --profile multilingual-cpu
polymorph prepare ./incoming-file target.schema.json --modelsBoth current model profiles are research-only for product use. Their provenance and dataset terms are documented in Model profiles. The standard bootstrap downloads neither model.
Models receive schema descriptors, not record payload values. Their evidence may improve ordering and reduce review work, but automatic promotion still requires independently strong deterministic evidence.
And currently, on the checked-in synthetic regression corpus, the model paths make the same automatic decisions as the deterministic profile.
| Profile | Result | Wall time | Peak RSS |
|---|---|---|---|
| Deterministic | Same decisions | 4.02 ms | 73.30 MiB |
| Encoder | Same decisions | 0.926 s | 284.52 MiB |
| Encoder + reranker | Same decisions | 2.325 s | 437.87 MiB |
The encoder run produced 100% observed automatic precision on 17 of 17 automatic decisions and 89.47% automation coverage among explicitly eligible fields. That is regression evidence on a synthetic corpus, not a claim of universal mapping accuracy.
So yes, the model stack works.
On this corpus it currently converts more electricity into heat without improving the decision set. We keep measuring instead of assigning architectural importance to fan noise.
See Measured performance baseline.
pip install -e ".[fileid,csv-detection,benchmark]"
polymorph inspect auto ./unknown-upload --magika
# Strict Linux boundary. Fails closed when compatible Bubblewrap is unavailable.
polymorph inspect isolated-content ./unknown-upload
polymorph doctor
polymorph benchmark inspect ./unknown-upload --records 10000 --magika
polymorph benchmark parser-worker ./unknown-upload --runs 5
polymorph benchmark mapping ./benchmarks/safety-regression.json \
--require-auto-precision 1.0 \
--require-automation-coverage 0.70
polymorph benchmark workflow --records 1000 --batch-size 100 \
--work-dir ./workflow-run --output workflow.json
polymorph recipe health
polymorph audit summary ./audit.sqlite
polymorph events check ./workflow-run/operational-events.jsonl --run-id RUN_ID
polymorph explain write_outcome_unknownRUN_ID is the workflow.observability.run_id value in workflow.json.
The benchmark commands are explicit diagnostics. Normal production paths do not enable CPython allocation tracing or RSS polling merely so a graph can feel involved.
On 2026-09-12, five fresh-workflow-state runs of the fully durable, signed and audited local transport moved 1,000 five-field records at 367.99 to 376.81 records/s, with a median of 375.83 records/s, using a SQLite destination and batches of 100. Median measured wall time was 2.661 s and median sampled peak RSS was 81.33 MiB on Windows build 26200, Python 3.11.9 and an Intel Core i9-12900K.
Each run included recipient-certificate verification, X25519 and ChaCha20-Poly1305 sealing, Ed25519 source signatures, durable source outbox, relay validation and fenced leases, destination authentication and decryption, contract validation, SQLite writes, sealed-spool cleanup, signed hash-chain audit and acknowledgements.
All 1,000 records were delivered once in every retained run. The exact five observations and
measurement metadata are checked in as
benchmarks/results/workflow-windows-20260912.json.
The deterministic workflow benchmark does not load the optional mapping models. A separate
500 ms nvidia-smi sample over two of the same runs observed total device memory remain at
2,788 MiB on an RTX 3080 with 10,240 MiB: 0 MiB observed change. GPU utilization was
device-wide and cannot be attributed to this process. Model benchmarks must report VRAM
separately.
See Performance baseline for the exact machine, methodology and caveats.
The new local toolkit closes practical integration gaps without turning evidence into authority:
polymorph-kit connector scaffold "Acme CRM" --output .\acme-connector
polymorph-kit quality inspect .\incoming.csv --output .\quality.json
polymorph-kit ui export .\review-ui
polymorph-kit review finalize .\review-draft.json --output .\review.json
polymorph-kit sync inspect .\.polymorph\sync.sqlite3The review component is dependency-free, light-mode, responsive, bilingual, and framework-neutral. Its drafts contain schema metadata but no rows and carry no write authority. Cleaning plans are explicit and lazy. OAuth access tokens stay in memory. Recurring cursors advance only after a structured completed write. See the ecosystem kit, embedded review UI, recurring runs, and the enterprise readiness facts.
- Architecture
- Reliability model
- Product direction
- File trust gate
- Parser isolation
- Recipes
- Benchmarking
- Workflow and failure lab
- Measured performance baseline
- Protocol
- Threat model
- Operations and replay safety
- Operational visibility
- Recipient key authenticity and rotation
- Security coverage
- Ecosystem and dataset review
- Operator workflows
- Test strategy
- Roadmap
- Security reporting
- Contributing
- Licensing explained
- Name and logo policy
Polymorph remains an alpha.
Inspection, mapping, planning and preflight are available through the CLI. Source transport, relay and destination delivery are tested Python APIs. Long-running agent services, an authenticated control channel and a deployment supervisor are still roadmap work.
Important current limits:
- Strict Linux containment currently covers the content-inspection worker only.
- Structured schema parsing and record iteration still execute in the local process after the content gate accepts a file.
- Windows workers use Job Object resource containment for CPU, memory and process-tree limits. They still provide no filesystem or network sandboxing.
- There is no durable trust-bundle distribution yet.
- There is no cumulative per-tenant relay queue quota yet.
- There is no external audit checkpoint protecting against log-suffix truncation yet.
- Raw database URLs may leak credentials through shell history. Production automation should use
DatabaseEndpointplus a secret provider. - Automatic-promotion policy still needs a large, source-separated adversarial corpus and independent security review.
- Operational event coverage is incomplete outside the currently instrumented workflow.
- Destination audit remains optional.
- There is no notification service or queue metric exporter yet.
The project documents these limits because "alpha" is a software maturity label, not a decorative badge we remove when the README starts looking expensive.
Polymorph is open source under the GNU Affero General Public License version 3 only (AGPL-3.0-only).
Commercial use is allowed under the AGPL. If those terms work for your use case, no separate paid license is required. A separate commercial license is available for proprietary or closed-source use and other use cases that need different terms.
Open source does not mean authorless. Copyright and license notices remain part of the project, and modified versions must follow the notice and source obligations in the AGPL.
See LICENSING.md for the human-readable overview, COMMERCIAL.md for alternative commercial licensing, and TRADEMARKS.md for the Polymorph name and logo policy.
Copyright 2026 Angus Uelsmann · angusu.de · NOTICE
Third-party components retain their own licenses; see THIRD_PARTY.md.
On an Intel Core i9-12900K with Windows and Python 3.11, the final full Polymorph Run acceptance check measured three integrity-gated 1,000-row workflow samples at 357.75-373.56 records/s (median 373.29), 2.677-2.795 s wall time (median 2.679), and 80.88-81.23 MiB peak RSS. The dedicated five-run baseline remains the less noisy headline measurement at 367.99-376.81 records/s (median 375.83); raw results and limitations are in benchmarks/results/workflow-windows-20260912.json.
The 0.4.0a3 streaming CSV rewrite copied 500,000 existing rows and appended 500,000 generated rows into a 23,277,791-byte file in 2.704 s: 184,939 appended rows/s with a measured RSS increase of 9,613,312 bytes over the 38,072,320-byte baseline. This is a local single-run Windows measurement, not a cross-platform storage guarantee.
Two post-fix GPU samples observed device-wide allocated VRAM staying at 2,788 MiB, an observed delta of 0 MiB. This is not process-attributed telemetry and does not prove that unrelated applications used no GPU. The fixed-validation advisory ranking comparison improved from 47/84 untrained to 61/84 trained, with 14 paired gains, zero paired regressions, zero unsafe automatic decisions, and unchanged deterministic authority. It is synthetic validation, not an independent production holdout.
- Download the current source as a ZIP
- Download the tested v0.4.0a11 pre-release, wheel, sdist, and Polymorph Run 1.1.0 buddy
Every supported buddy run refreshes the append-only local history under .polymorph/metrics. The tracked SVG and JSONL are published only after an explicit review with python scripts/run_metrics.py --project "D:\polymorph" --export-public; the command never commits or pushes. See the metrics history contract.
New users should begin with START-HERE.md. On Windows, the complete local proof is:
git clone https://github.com/IamAngusU/polymorph.git
cd polymorph
py -3.11 -m venv .venv
.venv\Scripts\python -m pip install ".[benchmark]"
World-Benchmark.cmdThe proof does not upload data, activate a model, push to GitHub or start GitHub Actions. Parquet, PostgreSQL and metadata-only review timing are documented in the start guide.
Install both commands into the same environment and Polymorph finds the guard on PATH:
python -m pip install "https://github.com/IamAngusU/polymorph/releases/download/v0.4.0a11/polymorph_bridge-0.4.0a11-py3-none-any.whl" "nsfw-guard[cpu] @ https://github.com/IamAngusU/nsfw-guard/releases/download/v0.1.0a3/nsfw_guard-0.1.0a3-py3-none-any.whl"
polymorph guard --doctor
polymorph guard C:\images\sample.jpgOne warm child process handles the stream. Paths are allow-listed automatically, every request is SHA-256-bound, responses are bounded and malformed protocol output stops the run. CPU is the default. A GPU is used only when requested, because surprise VRAM allocation is not an onboarding feature.
The products remain separate. NSFW Guard provides image-policy evidence; Polymorph validates the process contract. Neither mistakes a classifier score for permission to write anything.

