diff --git a/.github/workflows/weave.yml b/.github/workflows/weave.yml new file mode 100644 index 00000000..bb380c15 --- /dev/null +++ b/.github/workflows/weave.yml @@ -0,0 +1,34 @@ +name: Weave modular experiments +on: + pull_request: + paths: + - 'research/weave/**' + - '.github/workflows/weave.yml' + push: + branches: [main] + paths: + - 'research/weave/**' + - '.github/workflows/weave.yml' + workflow_dispatch: +permissions: + contents: read +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Fetch exact source without unpinned action dependencies + env: + SOURCE_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + run: | + set -euo pipefail + git init . + git remote add origin https://github.com/The-Interdependency/stack.git + git fetch --depth=1 origin "$SOURCE_SHA" + git checkout --detach FETCH_HEAD + test "$(git rev-parse HEAD)" = "$SOURCE_SHA" + - name: Verify assembly and provisional transport, not native cipher security + run: | + set -euo pipefail + python3 -VV + PYTHONDONTWRITEBYTECODE=1 python3 research/weave/test.py --receipt "$RUNNER_TEMP/weave-check.json" + cat "$RUNNER_TEMP/weave-check.json" diff --git a/AGENTS.md b/AGENTS.md index 8e4a19bf..b855f116 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -23,6 +23,7 @@ projects may later graduate into their own repositories. substituted a recursive pairing/authenticated codec for Erin Spencer's intended multi-arity interleaving construction. Do not extend it or treat any passing test, vector, decoder audit, or measurement as evidence for the intended URPCS design. +- `research/weave/` owns new Stack-local research into the full intended encryption system. Preserve all declared layers: hyperspace/gonol plaintext construction, private-gonol recovery, multiple reconstruction-dependent threads, thread-associated corpus/material, multi-arity last/first interleaving, and the intended asymmetric public/private relation. Read `SPECIFICATION.md` before implementation. Unresolved mechanics must fail closed as `hmmm`; do not reduce the project to its interleave layer or substitute a familiar cipher, codec, permutation generator, or URPCS mechanism. - `research/zfae/` owns Stack-local inference-construction experiments. ZFAE retains conceptual authority, PTCNA owns neural construction, and a0 owns runtime integration. Keep a0-betatest comparisons separately attributed. @@ -137,6 +138,9 @@ coherence; it does not replace workspace behavioral tests. independent repository/release authority boundary; exact UCNS affixiation geometry is unresolved. - URPCS is retired for specification divergence. Why GPT substituted the different architecture remains `hmmm`; its retained evidence is historical and implementation-local. +- Weave is specification-first research. Uneven partitions, stage-input + composition, schedule encoding, thread/corpus binding, hyperspace/gonol binding, + authentication/state, and the threat model remain `hmmm` until explicitly resolved. - The complete root `skill-lib/` snapshot refresh remains separate because the current provenance-bound fresh-making doctrine is newer than the local generator snapshot. - Project graduation automation remains unimplemented. diff --git a/README.md b/README.md index 5910b372..4da26a20 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ stack/ │ ├── edcm/ # current EDCM measurement research + BASE.json │ ├── pcea/ # current PCEA research + BASE.json │ ├── urpcs/ # RETIRED: substituted GPT-produced codec; historical evidence only +│ ├── interlace-encryption/ # intended multi-arity interleaving encryption research │ ├── ptcna/ # current PTCNA research + BASE.json │ ├── zfae/ # inference construction research and gonol input parser │ ├── epac/ # historical forge evidence; active implementation is independent @@ -101,10 +102,11 @@ multi-arity interleaving construction during GPT-assisted implementation. The re for the substitution is unresolved. Its tests and receipts apply only to that substituted codec and must not be used to validate or falsify the intended URPCS design. +The replacement research path is `research/weave/`. It owns the full intended encryption construction: hyperspace/gonol plaintext representation, private-gonol recovery, multiple reconstruction-dependent threads, thread-associated corpus/material, multi-arity bit interleaving, and the eventual asymmetric public/private relation. The interleave is one layer, not the project identity. The workspace deliberately contains no URPCS implementation dependency and makes no security claim. + ```bash -python3 research/urpcs/urpcs_v1_reference.py --self-test -python3 -m unittest discover -s research/urpcs/tests -p 'test*.py' -node research/urpcs/tests/test_independent_decoder.js +cat research/weave/SPECIFICATION.md +cat research/weave/BASE.json ``` ### Change stack structure @@ -137,6 +139,8 @@ Python Gonol Construction is likewise stack-local and ungraduated; its Python 3. constructor is an implemented candidate, not stack or language canon. URPCS is retired; its retained vectors and replay evidence establish behavior only of the substituted historical codec. No URPCS claim may be inferred from them. +Weave is new stack-local research and remains specification-first; no +confidentiality or production-security standing is implied by its placement. Psychsocio metafauna and From Photons to the Macroverse remain stack-local pre-graduation research. EPAC has an independently published MPL-2.0 `v0.1.0` release and has passed public Stack reconsumption. Its Python forge copy is retired; diff --git a/STACK_MANIFEST.md b/STACK_MANIFEST.md index 2030d498..e12465a2 100644 --- a/STACK_MANIFEST.md +++ b/STACK_MANIFEST.md @@ -14,10 +14,11 @@ Provenance and authority-boundary record for `The-Interdependency/stack`. - EPAC-derived carrier workspace separation UTC: `2026-09-18` at `545e135e2efbbcf29f033ab5530ac4876a68b718` - URPCS v1 workspace integration UTC: `2026-09-20` from Stack baseline `a428a41a38a8b30bacb7025a4d54070b228a8089` - URPCS retirement UTC: `2026-09-28`; specification divergence discovered after conversation-history review; retained implementation is historical evidence only +- Weave workspace creation UTC: `2026-09-28`; new specification-first research starts from the intended multi-arity interleaving mechanism without URPCS inheritance - English four-view complete-corpus audit UTC: `2026-09-22`, exact view source `1f9a35eb355296fc88d09784c7a3e2e95511ca31`; all 164,864 words, native and independent agreement. - Stack-manifest schema: `the-interdependency.stack-manifest` version `1.1.0` - Work-graph digest (SHA-256 over canonical `repositories` + `research_participants` + `boundaries` JSON): - `1e9a8f485c290bf7e3537e66cc72ac8ced749ce58fd4005647bd3981e071af1f` + `fa81adcbc37d2042bd6691857f3f97c26f8a16f89821bef66b8331df9553d251` - Machine-readable copy: [`stack-manifest.json`](stack-manifest.json) ## Directory contract @@ -61,7 +62,8 @@ release identity. |---|---|---|---|---| | `research/epac/` | `The-Interdependency/stack` | `0e8384bbb60e4c2189016a212bdd0030d04aed7d` | historical forge evidence; active implementation consumed from the independent EPAC release | no | | `research/epac-derived-carrier/` | `The-Interdependency/stack` | `545e135e2efbbcf29f033ab5530ac4876a68b718` | active stack-local carrier audit consuming exact EPAC source; no EPAC implementation/public-contract or scientific standing transfer | no | -| `research/urpcs/` | `The-Interdependency/stack` | `a428a41a38a8b30bacb7025a4d54070b228a8089` | stack-local authenticated recursive pairing codec research with byte-exact reference vectors; no confidentiality, PCEA compatibility, or UCNS-gonol identity transfer | no | +| `research/urpcs/` | `The-Interdependency/stack` | `a428a41a38a8b30bacb7025a4d54070b228a8089` | retired historical evidence for the substituted recursive pairing/authenticated codec; not evidence for the intended URPCS construction | no | +| `research/weave/` | `The-Interdependency/stack` | `01f340ba5b4c5f107703233750768e201a41da68` | new specification-first research for the intended multi-arity interleaving encryption construction; no URPCS implementation inheritance or security standing | no | | `research/english-gonol/` | `The-Interdependency/stack` | `99b3598b02a6e683b3c84184d8ea443b12fc0e1a` | stack-local English lexical/gonol construction separated from EDCM; full pinned-corpus v2 build/replay survived; consumes UCNS geometry; EDCM may evaluate outputs but does not define construction | no | | `research/python-gonol/` | `The-Interdependency/stack` | `0e8384bbb60e4c2189016a212bdd0030d04aed7d` | stack-local bottom-up Python 3.12 source gonol construction; applies METAPAT affixiation semantics, consumes optional UCNS geometry, and transfers no language authority to UCNS or EDCM | no | | `research/ucns/` | `The-Interdependency/ucns` | `1975fe70cf4e0826a8020c2da3047569e277af64` | explicit source base for integrated stack-local UCNS research; does not refresh or replace the manifest-pinned `libs/ucns` canonical view | no | @@ -129,6 +131,8 @@ construction. The reason for that substitution remains unresolved. All retained vectors, decoder evidence, and measurements describe only the substituted codec; they do not validate or falsify the intended URPCS design. +The intended construction now has a separate research owner at `research/weave/`. Weave preserves the full system rather than naming only the interleave layer: hyperspace/gonol plaintext construction, private-gonol recovery, multiple reconstruction-dependent threads, thread-associated corpus/material, multi-arity bit interleaving, and the intended asymmetric public/private relation. The workspace starts with no implementation inheritance from URPCS and no confidentiality or production-security claim. + ZFAE construction research lives at `research/zfae/`. Its exact research-only inputs are recorded above. The parser consumes the existing English GlyphGonol producer at the Stack baseline and the separately pinned UCNS carrier. Its @@ -201,12 +205,16 @@ fixtures remain executable historical evidence for the substituted profile only. No result from that profile may be promoted to a claim about the intended URPCS construction. +Weave is a new stack-local research component. It is not yet an +independent repository, release, or security-reviewed cryptosystem. + ## hmmm - UCNS has no `LICENSE` file at pinned commit `828c0b8`. - English Gonol Construction remains stack-local research; independent repository/release authority and the exact UCNS displacement law have not been established. - Python Gonol Construction remains stack-local research; independent repository/release authority and exact UCNS affixiation geometry remain unresolved. - URPCS is retired for specification divergence; why GPT flattened/substituted the intended construction remains unresolved, and retained evidence is scoped only to the substituted implementation. +- Weave preserves the intended mechanism, while uneven partitioning, stage-input composition, schedule encoding, thread/corpus binding, hyperspace/gonol binding, authentication/state, and the threat model remain unresolved. - `skill-lib/` remains a special operational snapshot at stack root rather than following the ordinary `libs/` + `research/` pair. ## UCHC input candidate diff --git a/research/weave/ASSEMBLY.md b/research/weave/ASSEMBLY.md new file mode 100644 index 00000000..d0adc9c4 --- /dev/null +++ b/research/weave/ASSEMBLY.md @@ -0,0 +1,130 @@ +# Weave: full modular assembly v1 + +Status: ASSEMBLY IMPLEMENTED; COMPLETE ENCRYPTION NOT IMPLEMENTED. + +This is a source-bound composition contract and switchable runner, not a replacement +cipher. The native key, binding, thread, corpus and join laws remain explicit missing +operators. No dummy implementation is enabled, and a missing operator never acts as +an identity. All-on encryption refuses before touching input until those operators +are supplied. Existing interleave code is reused without changing its findings. + +Source: The-Interdependency/stack@2f1a70948fb90f340613c49d27dfeb1b4e1b650f, +research/weave/SPECIFICATION.md. This specification preserves the full architecture. +The order below is an ASSISTANT PROPOSAL, not an additional user requirement. + +## Module plan and complete composition + +Key setup is separate from the reversible data path. `stages/key.py` owns KeyGen +and the public/private operation contract. The sender-side context has no private-key +field. This API separation does not prove that a public key reveals no private data. + +Proposed forward order: + +1. `stages/gonol.py`: construct the complete native nested plaintext object. +2. `stages/bind.py`: apply the public-side native relation whose recovery uses the + private gonol. This is a mathematical operation, not a password check. +3. `stages/split.py`: form the mutually required logical thread streams. +4. `stages/corpus.py`: apply the actual corpus/material relationship to each thread. +5. `stages/inter.py`: execute the exact supplied section partitions, last/first inward, + in supplied stage order, independently for each thread. +6. `stages/join.py`: interlace the transformed threads under the selected relation. +7. `stages/whole.py`: apply the last/first inward operation to the resulting whole. +8. `stages/auth.py`: optional integrity/replay contract; no authentication law has + been selected or implemented. Off by default; never a substitute for encryption. + +Inverse: undo the enabled data stages in the exact reverse order, using the recipient +context and the same explicitly selected profile. Key generation is not inverted. +A key-derived plan may govern any of these stages; it is not deferred until the end. +If inverse planning depends on data that is still hidden by a later inverse stage, +that dependency must be resolved by the native law, not circularly read from plaintext. + +## Switch semantics + +All seven required data stages and key setup default ON. Auth defaults OFF because +it has not been made a requirement of this design. Each has an independent Boolean. +OFF skips exactly that stage and its inverse, without changing any other switch. +Key OFF bypasses KeyGen and withholds both key objects from stage contexts. +A missing enabled implementation is BLOCKED, not skipped. A type/dependency that cannot +survive a bypass is an incompatible ablation, not a falsification of the full system. + +Every run carries its profile, stage order, switch mask, operator identities and +classification. The runner retains no plaintext, private key, corpus bytes or per-stage +payloads in its trace. It never serializes a plan or secret into ciphertext on its own. +Disabling a required stage creates ABLATION_ONLY output. An all-on experiment is still +FULL_PROFILE_EXPERIMENT, never a proof of security. Whole-system observations require +whole-system operators; test fixtures cannot authorize full-profile execution. + +## Questions and proposed answers + +The short IDs below let Erin change answers without retyping the project. None of +these proposed answers is presented as a previously supplied law. + +| ID | Question necessary to complete the binding | Proposed answer / research position | +|---|---|---| +| Q1 | Does the plaintext construction use a lossless binary admission into native gonols, a language-domain construction, or both? | Support the entire raw input without lossy normalization. Use a native binary admission for arbitrary files and explicitly selected language constructors for language-aware profiles. Do not relabel a JSON tree or an input dossier as a new gonol. The binary and whole-message constructors still need an owning native implementation. | +| Q2 | Is the private gonol a missing origin/attachment relation, an omitted member, or something else; what public operation corresponds to it? | Investigate a public projection of the full native relation with a private origin/attachment supplying the lift during recovery. This is a candidate target, not an established algebra. Deriving its forward operation and testing unauthorized inversion is the research job; Erin need not supply a security proof. | +| Q3 | Are threads different necessary parts/projections of one plaintext construction, or separate full constructions? | Different required parts of one construction, preserving cross-thread relations and occurrence identity. No independent plaintext copy in each lane. Whether every lane is mathematically necessary must be measured, not enforced by an artificial missing-file check. Exact routing/projection remains to be defined. | +| Q4 | Does corpus material supply axes/origins, traversal instructions, transformed values, or a combination? | Keep two explicit candidates: corpus-derived native axes/origin attachments; and corpus-derived traversal/arity choices. Use actual content in either case. Evaluate both within the complete composition; do not replace either with a filename hash, XOR mask or unrelated cipher. | +| Q5 | How do thread streams interlace, and is final whole-stream interleaving after that join? | Use a declared key/context-derived thread route, followed by the final last/first interleave over the joined stream. This is one proposed order. A per-thread final pass is a separate alternative, not an invisible extra pass. | +| Q6 | Do successive arities consume the previous stage's output, and how are remainders handled? | Proposed sequential composition. Supply exact partition lengths for every stage/lane now; balanced quotient/remainder partitioning is one selectable future policy, not an assumed rule. Keep arity distinct from level count: no invented three-stage minimum. | +| Q7 | What may an independent sender know: just public key/material, or any private corpus selections and schedules? | Sender gets public key, allowed public material and message randomness only. Private gonol and secret selections remain recipient-side. Any secret-dependent stage must offer a public forward operation. Giving encryption the private plan would be a symmetric test, not the requested asymmetric system. | +| Q8 | Are thread count, partitions, corpus locations and join route message-dependent, key-dependent or explicitly supplied? | Keep them key-and-message-context dependent candidates. The public/native forward law must make encryption possible without disclosing the private inverse. Do not publish them as convenient ciphertext headers before deciding which are intended to remain hidden. | +| Q9 | Should equal plaintext under the same public key produce different outputs? | Proposed yes: explicit fresh sender randomness, reproducible only in test fixtures. Derive its role through the native relation; a public nonce alone does not create secrecy or prevent key recovery. No automatic randomization mechanism is inserted here. | +| Q10 | Which ciphertext structure is public, and must altered or incomplete messages be rejected? | Specify version, profile negotiation, length policy, integrity and replay policy before a wire format is frozen. Keep experimental switch masks in the lab receipt; do not silently disclose secret structure. There is no selected authentication construction. | + +Q2, Q4 and Q8 require mathematical candidate derivation as well as intent. A label such +as 'private projection' is not an implementation. Operators remain unbound until that +actual transformation exists. The original pre-substitution conversation remains +unrecovered; this does not establish that Erin never specified these relations. + +## Usage + +From `research/weave/`: + +```bash +python run.py # full proposed profile: explicit BLOCKED list +python run.py --off corpus # one-stage ablation; no cascading skips +python run.py --on auth # also requires a real authentication law +python -m unittest discover -s tests -p 'test_assembly.py' +``` + +`assembly.Pipeline` accepts explicitly supplied `Operator` bindings. Nothing is loaded +from ciphertext or dynamic module names. Native objects pass through without conversion +into a generic replacement tree. `stages/inter.py` expects a `Streams` object plus +explicit per-thread partitions; `stages/whole.py` expects exactly one joined stream. +KeyGen is explicitly supplied through `stages/key.py`, not invented by the runner. + +## Verification and remaining work + +Tests cover assembly control flow, inverse order, independent switches, missing-law +refusal, public/private API separation, and the two existing literal bit operations. +They do not simulate missing native operators and call that encryption. Test-marker +operators are labelled wiring fixtures and cannot run as full-profile evidence. +A valid decryption API returning original data is not by itself security evidence. + +Deployment: manual local research only; no service, scheduled work or network calls. +Rollback: remove this new assembly and its tests; preserve the original source/spec +and sealed interleave evidence. No imported `libs/`, upstream producer or lifecycle +standing changes in this transaction. + +## hmmm + +The assembled interface is executable. The complete cipher is not: native message +admission/composition, private/public binding, thread/corpus coupling and join/key +planning are not filled with pretend implementations. These are the next exact +research/intent decisions exposed by this assembly. + +## Native source inspection + +UCHC `hyperspace_construct.py` blob `8b55823805c87ad8c4cc9d7451dc2eb90bdedd3a` +supplies real glyph/word/definition construction and recovery. UCHC +`inference_input.py` blob `0959342f9526e8c0b35cc7e1a2a448b0961977b9` explicitly +calls `InferenceFrame` an input dossier, not a new gonol. That dossier is not silently +substituted for a native whole-message encryption gonol. The full construct database +was not available in this runtime, and no reduced fabricated corpus was generated. + +Skill application record (one unit of work): `the-interdependency`, +`meta-module-build`, `msdmd`; source preservation, explicit native boundaries and +fail-closed module wiring applied. Outcome: assembly tested, full cipher unresolved. +METAPAT README consulted for domain-owned mechanisms/non-transfer; no cryptographic +conclusion is inferred from that semantic source. diff --git a/research/weave/BASE.json b/research/weave/BASE.json new file mode 100644 index 00000000..4d9d97f2 --- /dev/null +++ b/research/weave/BASE.json @@ -0,0 +1,10 @@ +{ + "schema": "the-interdependency.stack-research-base", + "version": "1.0.0", + "project": "weave", + "source_repository": "The-Interdependency/stack", + "source_commit": "01f340ba5b4c5f107703233750768e201a41da68", + "canon_path": null, + "authority": "stack-local specification and research for Erin Spencer's full intended multi-layer encryption construction", + "standing": "stack-local-research" +} diff --git a/research/weave/IMPLEMENTATION_PLAN.json b/research/weave/IMPLEMENTATION_PLAN.json new file mode 100644 index 00000000..09d3cc6c --- /dev/null +++ b/research/weave/IMPLEMENTATION_PLAN.json @@ -0,0 +1,19 @@ +{ + "schema": "weave.provisional-implementation-plan/v1", + "source": "The-Interdependency/stack@200032dba6131145ebc61b52bb4f1aee507e6f63", + "instruction": "Implement what can be with available answers; expose questions and probable answers for correction, improvement or approval.", + "claim": "Executable modular transport candidate and native-source adapter; not the complete asymmetric Weave construction.", + "selected_provisional_answers": { + "partition": "Sequential balanced quotient/remainder; retain empty sections; no minimum stage count.", + "split": "Explicit occurrence-to-lane route; cyclic routing is a named transport-only example, not the missing native complementary-thread law.", + "corpus": "Named traversal candidate: actual selected corpus bits choose left/right consumption of lane occurrences; inverse recomputes independently from material.", + "join": "Explicit lane visitation order repeated until exhausted; preserve each lane's internal order.", + "native": "Read/recover existing native UCHC word/glyph/definition objects at the inspected source identity; do not substitute a general plaintext gonol.", + "unknown": "Native key/binding, whole-message admission, native thread projection and corpus-origin geometry remain unimplemented." + }, + "files": ["stages/api.py", "assembly.py", "stages/split.py", "stages/corpus.py", "stages/inter.py", "stages/join.py", "stages/gonol.py", "transport.py", "lab.py", "tests/test_transport.py", "profiles/transport.json", "IMPLEMENTED.md"], + "boundaries": {"network": "none during execution", "files": "CLI reads explicitly supplied paths and writes new output files only", "secrets": "no private key/corpus bytes in receipts", "authority": "no upstream producer or libs changes"}, + "verification": ["native refusals", "exact inverses", "all switch combinations", "actual corpus contribution", "separate-process recovery", "wrong-material and malformed-input behavior", "no source/trace-assisted inverse"], + "rollout": "Explicit transport profile only; full profile remains default and refuses unresolved laws.", + "rollback": "Revert this workspace-local commit; retain sealed run-1 evidence." +} diff --git a/research/weave/IMPLEMENTED.md b/research/weave/IMPLEMENTED.md new file mode 100644 index 00000000..99d2f3f1 --- /dev/null +++ b/research/weave/IMPLEMENTED.md @@ -0,0 +1,143 @@ +# Weave — executable provisional stages + +**Delivered:** a five-stage transport experiment, each stage independently switchable, +plus a source-pinned native-word adapter. **Not delivered:** a complete native asymmetric +Weave cipher. The all-on native profile remains the default and reports unresolved +operations before processing input. No native operation is silently replaced. + +This work implements the instruction to act on available proposed answers and return +remaining questions for correction, improvement or approval. The concrete corpus +left/right formula, cyclic split example and cyclic join example are explicitly new +assistant-selected trial rules. They are not attributed to Erin or made canonical. + +## What executes + +| Module | Implemented operation | Standing | +|---|---|---| +| `stages/split.py` | Route each source bit occurrence exactly once to an explicitly assigned lane; reconstruct original order. | Transport candidate; native complementary-gonol thread law remains open. | +| `stages/corpus.py` | Use actual selected corpus bits to choose left/right consumption of each lane; recompute the inverse from the same material. | New provisional traversal variant, not native origin/axis binding. | +| `stages/inter.py` | Sequential last/first section operations, with exact supplied boundaries or balanced quotient/remainder generation. | Literal operation with an explicitly provisional partition policy. | +| `stages/join.py` | Interlace lanes in a supplied repeating visitation order; recover exact lane lengths and order. | Explicit transport route, not a native private route generator. | +| `stages/whole.py` | Last/first inward interleaving across the entire joined stream. | Existing literal operation preserved byte-for-byte. | +| `stages/gonol.py` | Verify the inspected UCHC source blob and caller-pinned database, then use actual `promote_word`/`recover_word` objects. | Adapter written; missing-input/source-refusal tested; successful native corpus replay NOT executed here. | +| `stages/key.py`, `stages/bind.py` | Existing explicit interfaces and missing-law refusal. | Native key generation and private-gonol recovery algebra unimplemented. | +| `stages/auth.py` | Independent optional switch; enabled use requires a real supplied law. | Authentication/replay unimplemented. | + +The runnable profile composes **split → corpus traversal → arity interleaves → join → +whole interleave**, and reverses that composition. It acts on explicitly admitted +transport bits. It does not call those bits a gonol or relabel this profile as Weave. + +## Run from a phone-oriented Python environment + +Python 3.11+ and its standard library are sufficient for the transport experiment. +The local evidence here was produced with Python 3.13.5; Android execution was not +observed. From `research/weave/` or the extracted bundle: + +```sh +python run.py demo +python test.py --receipt /tmp/weave-check.json +python run.py +``` + +The first runs a nonsecret demonstration. The second runs all local tests. The third +shows the full native profile's unresolved laws and returns exit status 2. + +For actual file experiments, supply each selected material explicitly: + +```sh +python run.py enc profiles/transport.json input.bin output.lab \ + --material a=book.txt --material b=manual.txt --material c=sound.bin +python run.py dec profiles/transport.json output.lab recovered.bin \ + --material a=book.txt --material b=manual.txt --material c=sound.bin +``` + +This is **not safe storage for real secrets**. Files are never overwritten. Output +`.lab` files are research records, not a chosen production cipher format. They carry +switch/operator identities and packed bits; they do not carry corpus content or +secret native reconstruction state. The separate profile remains necessary input. +The message and each supplied material file are capped at 64 KiB as a declared runtime +resource guard, not a cryptographic or mathematical limit. The experiment also caps +thread count at 63 and each schedule at 64 stages/arity 4096 for the same reason. + +## Switches and absent dependencies + +`profiles/transport.json` explicitly disables `key`, `gonol`, `bind`, and `auth`. +It is selected only by demo/enc/dec. `run.py` without that command retains full native +scope. Every switch is a literal Boolean; unknown/misspelled fields fail. + +Off means that stage and its inverse do not run. Corpus off reads no supplied corpus +path, passes no corpus material to later stages and compiles no stale corpus control. +Other switches do not change automatically. Turning join off while leaving multiple +lanes and whole on is an incompatible configuration, not a result against the design. +Opaque third-party callbacks remain responsible for their declared dependencies; +this runner is not a sandbox against callbacks hiding private data in closures. + +All transport operators carry `scope='transport'`. Even if combined with additional +operators, their result cannot be classified as a full-native profile experiment. +Wiring fixtures remain separately labelled and disabled unless explicitly permitted. + +## Verification + +The source-bound receipt is `evidence/transport-v1.json`. Tests include original assembly +regressions, 512 assembly switch combinations, all 32 transport switch combinations +(28 executable; four incompatible), all 8,191 binary inputs of lengths 0–12, unequal and +empty sections, exact byte recovery at 64 KiB, and independent-process recovery after +deleting the original input file. There are no skipped tests. + +The corpus-content witness demonstrates a real effect: changing actual material changes +a declared case's output, while renaming identical material does not. Disabling corpus +removes that influence. This does not imply every material is distinct or adds entropy: +all-zero material selects left-to-right traversal, all-one selects reverse traversal, +and unused material bits have no effect. Equivalent traversals remain possible. + +The test suite also records **limitations**, not hidden successes: fixed-map recovery +succeeds against this complete transport candidate at 137 bits in eight selected-input +queries; wrong material can return incorrect bytes without an integrity error. These +are observations about the explicitly chosen transport variant, not the unimplemented +native gonol/private-key/corpus relations or a verdict on Weave. No independent +researcher reviewed these tests. Fresh-process recovery is not an independent-author +implementation. + +## Native source boundary + +The optional reader consumes UCHC `human/english/english_gonol/hyperspace_construct.py` +at Git blob `8b55823805c87ad8c4cc9d7451dc2eb90bdedd3a`. It verifies the exact source +before execution and opens a caller-digest-bound `construct.db` read-only. It returns +an actual upstream `WordGonol`, not a generic message tree. A hash match is source +identity, not full-corpus validation. No constructed corpus database was materialized +here, so successful native replay is explicitly NOT EXECUTED. No small fabricated +corpus is presented as a full-corpus replacement. + +Whole-message/binary admission, nested native serialization, private-gonol binding, +public/private key derivation and native complementary-thread construction remain +separate unfinished implementation/research work. The reader is not automatically +bound to the complete `gonol` stage because its admitted scope is narrower. + +## Questions and change control + +Read `QUESTIONS.md`: Q1–Q10 each pair an unresolved choice with a proposed answer, +current implementation status and the exact distinction that must survive correction. +Approving a research direction does not require Erin to supply its proof or code. + +## Provenance, deployment and rollback + +Starting Stack identity: `200032dba6131145ebc61b52bb4f1aee507e6f63` on existing PR #62. +No `libs/` snapshot, upstream constructor or root project lifecycle is modified. +The source file plan and non-transfer boundaries are in `IMPLEMENTATION_PLAN.json`. +The original run-1 probe and assembly test source were recovered byte-exactly and +checked against their Git blob identities before use. Old sealed receipts are not +rewritten as evidence for new code. + +Rollout is manual research plus path-scoped CI. Revert this workspace-local change to +roll back; retained historical evidence remains available. Usage of `the-interdependency`, +`meta-module-build`, `msdmd`, and `gonol-build` shaped scope, source ownership, native +metadata and validation. This records their contribution, not an invented maturity +count. The canonical skill-usage runner was not available in this local checkout; +its persistent usage counter was not updated. METAPAT's domain-owned-mechanism boundary +was consulted; no cryptographic result is inferred from it. + +## hmmm + +The coded proposals are ready to correct, improve or approve. The complete native cipher +remains unimplemented. Transport execution, source verification and API refusal do not +replace its missing constitutive relations. diff --git a/research/weave/PLAN.md b/research/weave/PLAN.md new file mode 100644 index 00000000..23840a6d --- /dev/null +++ b/research/weave/PLAN.md @@ -0,0 +1,74 @@ +# Weave source-bound evaluation, run 1 + +## Decision and whole-system boundary +Determine what the declared construction currently establishes, without replacing it. +The whole design includes nested hyperspace/gonol plaintext construction, a private +gonol required in recovery, multiple related streams, corpus/material participation, +section-local last/first interleaving, final interleaving and the intended asymmetric +public/private relation. A result on one operation is not a result on that whole. + +## Inputs frozen before execution +- Stack PR 62 head: 247af26527d0d76558dac1dd7fd05e41ce83112e. +- Its Weave directory contains BASE.json, README.md and SPECIFICATION.md; no implementation. +- UCHC hyperspace_construct.py blob: 8b55823805c87ad8c4cc9d7451dc2eb90bdedd3a. +- User's literal interleave: last, first, next-last, next-first, continuing inward; + section arities illustrated by 5, 7, 3, followed by interleaving the whole. +- User's 'arity three, minimum' does not establish a minimum of three stages. + +## Explicit experimental choices, not additional user laws +The composed-operation profile feeds one stage's output to the next, retains section +order, and uses explicitly supplied positive section lengths. Equal-partition cases +use lengths divisible by every selected arity: no invented remainder allocation. +Empty input is tested for the end-interleave primitive alone. Empty sections and +partial partition rules are not silently selected. + +The independent inverse uses a closed-form position formula, not the encoder's +implementation or a permutation exported by it. None of these operations is named +'encrypt', 'decrypt', 'gonol', 'keygen', or a complete Weave implementation. + +## Frozen checks and evidence domain +1. Exhaust all 8,191 binary inputs of lengths 0 through 12 for the end operation. +2. Exhaust all positive ordered partitions of lengths 3 through 12 with at least + three parts. Distinct position labels test bijection on every position, implying + the same recovery for any values occupying those positions. +3. Enumerate all schedules over (3,5,7) of lengths 1 through 8 on 105 positions. + Compare exact induced maps, record collisions and stage periods. Distinct + schedules are not presumed to define distinct maps. +4. Execute the stated (5,7,3) sequential profile on lengths 105, 210, 420, 840, + 8,400 and 67,200. Preserve position witnesses and exact inversion. +5. Attack the fixed-map profile at lengths 105, 840 and 8,400 using binary position + labels and no access to the schedule in the attack function. Confirm inferred + inverse on 64 held-out generated bitstrings per length. This attack assumes the + same positional map across queries. No transfer to message-dependent maps or + the unimplemented complete system. +6. Check weight preservation and constant-input witnesses. State the algebraic + scope: rearranging bits, not the complete gonol/corpus/private-key construction. +7. Exercise refusal of malformed inputs and missing whole-system implementation. + Readiness is not tested cryptographic strength; no substitute adapter is used. + +## Preflight and terminal condition +Python standard library only, deterministic fixtures, no network, no secrets, no +external paid API. The largest enumerated table is 9,840 maps on 105 positions. +Expected memory is well within this container's available memory; output is small. +Finish the complete declared finite domains or report execution failure. There is no +scientific wall-clock stopping rule. SHA-256 identifies evidence, not encryption. + +## Failure attribution +- A runtime disagreement in literal end-interleaving: implementation defect until + independently reproduced against the written law. +- A schedule collision: falsifies assistant-added schedule-uniqueness claims, not + the user's whole design. +- Fixed-map attack succeeds: that profile supplies no confidentiality against the + declared attack. It says nothing decisive about the absent required relations. +- Missing native relation: BLOCKED for full execution, never FALSIFIED. + +## Usage +Run `python probe.py > receipt.json`; run again and compare the exact output bytes. +Read REPORT.md with the receipt. Do not interpret a component pass as system approval. + +## hmmm +Original thirteen-law conversation export was not recovered. The retrieved uploaded +URPCS law files describe the retired authenticated codec, not the intended encryption. +Thread/corpus original details are present here only as conversation context, not a +recovered exact transcript. Public/private-gonol binding and full composition are not +implemented in the inspected Weave directory. No replacement has been invented. diff --git a/research/weave/QUESTIONS.md b/research/weave/QUESTIONS.md new file mode 100644 index 00000000..38da722d --- /dev/null +++ b/research/weave/QUESTIONS.md @@ -0,0 +1,102 @@ +# Weave — questions and provisional answers + +These are proposals for Erin to correct, improve or approve. Implementation does not +constitute approval. Existing Q1–Q10 identities from ASSEMBLY.md are retained. + +## Q6 — Stage composition and uneven partitions +**Question:** Should each arity act on the preceding output, with the remainder assigned +left-to-right? **Proposed answer:** Yes. First r sections get one extra bit; preserve +empty sections if arity exceeds length. Preserve supplied repetitions and allow one +stage. **Implemented provisionally**, independently reversible and tested. Arity is +not level count, and thread count is a third distinct parameter. + +## Q3 — What are the threads? +**Question:** Are they complementary native contributions from one full plaintext +gonol, rather than independent copies or arbitrary byte chunks? +**Proposed answer:** Complementary native contributions, retaining occurrence identities +and cross-thread relations. **Native rule still open.** Implemented now: exact +occurrence-to-lane routing and inverse; the demo chooses cyclic assignment. That is an +explicit transport witness, not a claim to have implemented the native projection or +proved every thread necessary. Repeated/structured inputs may be recoverable from less. + +## Q4 — What should the actual corpus do? +**Question:** Should material govern traversal, supply native axes/origin attachments, +change values, or combine these roles? +**Proposed answer:** Keep traversal and native-context variants distinct, and compare +both inside the eventual full construction. **A traversal trial is implemented:** +material bit 0 consumes the left end of the current lane, bit 1 the right end. Bits are +MSB-first, starting at the specified offset and repeating when exhausted. This exact +formula is a new assistant proposal, NOT a rule previously attributed to Erin. It +uses content, not a filename hash or XOR mask. Only the visited material bits influence +a run; unused material is not counted as secrecy. Native axes/origin binding remains open. + +## Q5 — Joining and the final operation +**Question:** How should transformed threads be interlaced, and when is the final pass? +**Proposed answer:** An explicit context-derived lane route, then one final last/first +pass over the whole joined stream. **Supplied routes are implemented.** The demo cycles +through [2,0,1], skipping exhausted lanes; it does not derive that route from a key. +A private/native route generator still needs its constitutive relation. + +## Q1 — Native plaintext admission +**Question:** Should one native admission cover arbitrary binary files, alongside +language-aware constructions for admitted text? +**Proposed answer:** Yes; preserve exact bytes without normalization. **Implemented: +a source-pinned read/recovery adapter for already-constructed UCHC words.** That returns +the actual upstream object, not a new label tree. General binary admission, +whole-message native composition and its transport serialization remain open. The +adapter's successful real-corpus path has NOT been executed in this session. + +## Q2 — Private gonol and public counterpart +**Question:** Does the private gonol restore a missing origin/attachment relation, +a member, an embedding, or another constitutive relation? +**Proposed answer:** First examine a private origin/attachment relation that selects +the recoverable embedding, paired with a public forward construction. **Unimplemented.** +The research task is to derive concrete native maps and test inversion; approval of +this direction is not a proof, and Erin is not being asked to supply one. No password +check, random permutation seed or conventional cryptosystem substitutes for it. + +## Q7 — What can an independent sender know? +**Question:** Must the sender operate using public-side material without recipient +private-gonol knowledge or private schedules? +**Proposed answer:** Yes, for the intended noninteractive asymmetric mode. Secret +corpus selections must have a usable public forward counterpart where needed. +**The API separates the two sides; the mathematical relation is not implemented.** +The transport experiment supplies the same explicit routing/material recipe to both +sides and makes no asymmetric claim. + +## Q8 — Control derivation and recovery order +**Question:** Are thread counts, routes, corpus locations and arities derived from the +native key/context rather than simply stored as a private plan? +**Proposed answer:** Derive them from native key relations plus available message +context. Reverse operations must obtain needed controls before they need to recover +the hidden content those controls protect. **Explicit control execution is implemented; +native derivation remains open.** Forward and recovery compile the transport recipe +independently; no encoder trace is supplied to recovery. + +## Q9 — Repeated-message variation +**Question:** Should repeated input under one public key produce distinct outputs? +**Proposed answer:** Yes: fresh per-message input should alter applicable native +relations/controls; a fixed value allows deterministic tests. **No native randomness +coupling is implemented.** Merely appending a nonce is not used as a substitute. + +## Q10 — Integrity, length and replay +**Question:** Must modified, incomplete and replayed messages be rejected, and which +metadata may be public? +**Proposed answer:** Require those properties for a usable final encryption profile; +retain a distinct integrity/state module and choose its actual mechanism explicitly. +**Unimplemented.** The lab format checks syntax, lengths and experiment identities, +but does not authenticate data. Wrong material or a same-length bit change can return +incorrect bytes without an integrity error. Lab recipe metadata is not a selected +production ciphertext format. + +## Usage + +Reply using the existing Q numbers, for example “Q4: corpus supplies origin attachments; +keep the traversal candidate only for comparison.” Each changed answer can be traced +to the owning module and tested without silently changing the others. + +## hmmm + +The working transport is not the desired native encryption construction. Questions +Q1–Q4 and Q7–Q9 carry the remaining native relations. Concrete rules, not renamed +placeholders or successful transport tests, will close those boundaries. diff --git a/research/weave/README.md b/research/weave/README.md new file mode 100644 index 00000000..85e39737 --- /dev/null +++ b/research/weave/README.md @@ -0,0 +1,139 @@ +# Weave + +Standing: **stack-local research; specification-first; no security claim**. + +[Executed source-bound results](REPORT.md) include literal-operation recovery, +schedule equivalences and a fixed-map attack. The complete design remains untested; +these results are not a whole-system verdict. + +Weave is the replacement research workspace for Erin Spencer's intended encryption +system after retirement of the substituted URPCS implementation. + +The system is **not** merely an interleaving permutation. The preserved architecture +contains multiple cooperating layers. Development may stage those layers for testing, +but no layer may be silently discarded or replaced by a familiar construction. + +## Preserved architecture + +### 1. Hyperspace / gonol plaintext construction + +Plaintext is intended to be processed into a hyperspace gonol construction rather than +treated only as an opaque byte string. The information is represented as a nested gonol +set before or as part of encryption. + +Recovery is intended to require a **private gonol** associated with the private key. +The exact reversible binding and key representation remain unresolved and must be +specified rather than invented. + +### 2. Multiple threads + +The encrypted construction uses multiple threads/data streams, with arity at least +three and contemplated thread counts including three, five, seven, or more. + +Each thread must contribute to reconstruction. A single independently useful stream is +not the intended construction. + +### 3. Thread-associated corpus/material + +Threads may be associated with user-selected corpus/material such as literary works, +music, technical manuals, sounds, or other chosen source material. + +The corpus/material is intended to participate in reconstruction as semi-secret +context, not merely as documentation or a label. Its exact derivation and binding rule +remain to be frozen. + +### 4. Multi-arity bit interleaving + +The bit transform is one load-bearing layer inside Weave. + +For an explicitly declared ordered sequence of division arities: + +1. divide the working bit sequence into the declared number of sections; +2. within each section interleave inward from opposite ends: + last bit, first bit, next-to-last bit, next-from-first bit, and so on; +3. repeat for the declared arity sequence, examples already given including fifths, + sevenths, and thirds; +4. after the declared levels, interleave the resulting whole sequence in the same + opposite-end manner. + +Stage count is distinct from arity. “Arity three, minimum” does not establish a +minimum of three stages; the unsupported assistant-added restriction is removed. + +Knowing the division choices, their order, and the number of levels is part of the +reconstruction problem unless an explicit later law changes that role. Different +schedule descriptions can induce the same map; measure equivalence rather than +assuming schedule uniqueness. + +### 5. Keying / recovery structure + +The intended system ultimately requires asymmetric recovery structure with no silent +dependency on an external cryptographic system. The private side is intended to include +the private gonol and the structural information needed to reverse the construction. + +The exact public/private derivation law is not yet specified. That absence is `hmmm`, +not permission to substitute RSA, ECC, Diffie-Hellman, a conventional KEM, or URPCS. + +## Non-inheritance boundary + +`research/urpcs/` is historical evidence of a substituted GPT-produced construction. +No URPCS law, codec, test, result, or security conclusion is an implementation +dependency of Weave. + +URPCS may be consulted only as a specification-divergence witness: an example of what +must not happen again. + +## Development discipline + +The full architecture is preserved from the start, while implementation proceeds in +separable layers so each contribution can be falsified. The order listed below is an +assistant proposal, not a user-selected dependency law; actual dependencies must come +from the complete source-backed construction. + +1. Freeze the complete dataflow and the inverse dependencies among gonol construction, + threads, corpus/material, interleaving, and key structure. +2. Specify exact bit-level interleaving and inverse rules without changing the mechanism. +3. Specify thread formation and recombination. +4. Specify corpus/material derivation and binding. +5. Specify hyperspace/private-gonol construction and recovery. +6. Specify the asymmetric public/private relation. +7. Implement the smallest complete round-trip profile containing every required layer. +8. Build an independently structured decoder/recovery implementation from the written + contract. +9. Perform adversarial analysis before making any confidentiality or production claim. + +A reduced test harness may isolate one layer, but results from a reduced harness may not +be presented as results for Weave as a whole. + +## Usage guidance + +Start here: + +```bash +cat research/weave/SPECIFICATION.md +cat research/weave/BASE.json +``` + +Reproduce the executed component evidence with Python's standard library only: + +```bash +cd research/weave +python probe.py > receipt.json +python probe.py --check receipt.json +python verify.py receipt.json +``` + +See [PLAN.md](PLAN.md) for the pre-execution scope and [REPORT.md](REPORT.md) for +results, mathematical derivations, exact assumptions and the expected receipt digest. + +Do not use Weave to protect real secrets until an explicit security contract and +adversarial evidence justify that use. + +## hmmm + +Uneven section partition selection; exact ordering/composition of arity stages; thread +formation; corpus/material derivation; hyperspace/gonol encoding; private-gonol binding; +public/private key derivation; authentication, nonce/state, replay behavior; and the +threat model remain unresolved. They are preserved as required design boundaries, not +optional features and not invitations for model substitution. The original pre-substitution +thirteen-law conversation was not recovered; a missing retrieved source is not evidence +that the user never supplied the relation. diff --git a/research/weave/REPORT.md b/research/weave/REPORT.md new file mode 100644 index 00000000..52c20a06 --- /dev/null +++ b/research/weave/REPORT.md @@ -0,0 +1,172 @@ +# Weave — first source-bound results + +**Full-design verdict: not established.** These are executed results for the specified +interleaving operation and an explicitly declared sequential composition profile. +The nested gonol, private-gonol, thread/corpus and asymmetric relations remain part of +the required design. They were neither simulated with substitutes nor evaluated by +transferring a result from the interleave. This is partial evidence, not completion of +the requested full-system assessment. + +## Executed results + +The literal last/first inward operation has an exact inverse. It passed all **8,191 +binary inputs of lengths 0 through 12**. Section-local operation and its inverse +passed **4,017 ordered positive partitions**: every partition of lengths 3 through 12 +having at least three sections. Position-labelled tests prove each tested map is a +bijection for any bit values placed at those positions, not just one test plaintext. + +The sequential `(5,7,3)` profile, including its final whole-sequence interleave, +recovered every position exactly at **105, 210, 420, 840, 8,400 and 67,200 bits**. +No one-byte implementation limit was introduced. Uneven section sizes are supported +when supplied explicitly; no remainder-allocation policy was invented. + +At 105 bits, **all six orderings of the illustrated arities 3, 5 and 7 produce +different maps**. Thus order has a demonstrated effect in that declared domain. + +## Exact inverse + +For `y = I(x)`, input length `n` and valid output indices: + +```text +y[2r] = x[n - 1 - r] +y[2r+1] = x[r] + +x[i] = y[2i+1] when i < floor(n/2) +x[i] = y[2(n-1-i)] otherwise +``` + +The two index families cover every input position exactly once. This derivation +holds for any finite length, including the odd-length centre. Inverting each known +section reverses a stage; undoing the final interleave and then the stages in reverse +order reverses the explicitly sequential profile. + +## Schedule equivalences + +The complete predeclared census examined all **9,840 schedules** over `(3,5,7)` with +one through eight stages at 105 bits. It found **9,727 distinct positional maps**. +All 363 descriptions through depth five were distinct in this domain; repeated maps +first appeared at depth six. + +These schedules produce exactly the same map at 105 bits: + +```text +(5,7,3) +(5,7,3,7,7,7,7,7) +``` + +Consequently their outputs agree for **every** 105-bit input under the declared +equal-partition sequential profile. Five applications of the arity-seven stage +restore all positions. Exact single-stage periods here: **35 for arity 3, 7 for +arity 5, and 5 for arity 7**. + +Repeated arities were an experimental choice, not a recovered rule requiring your +design to permit them. The result falsifies the assistant-added universal claim that +changing stage count necessarily changes the transformation. It does not falsify +Weave, prescribe a key policy, or equate schedule counts with key strength. + +## Fixed-map attack + +For the fixed `(5,7,3)` profile, an attack given only input/output access and message +length recovered an equivalent inverse: + +| Message length | Chosen input queries | Held-out messages recovered | +|---|---:|---:| +| 105 bits | 7 | 64 of 64 | +| 840 bits | 10 | 64 of 64 | +| 8,400 bits | 14 | 64 of 64 | + +The attacker never receives the schedule. Query `j` places bit `j` of each position's +binary index at that position. Returned bits reconstruct the indices in output order. +The result is an **equivalent inverse**, not recovery of the actual schedule. + +**Assumption:** one fixed length-preserving positional map across queries and held-out +messages. No result is claimed for message-dependent maps, changing maps per encryption, +or the complete Weave composition. + +If a proposed composition of threads and corpus operations ultimately does nothing +but apply one fixed bit permutation, this argument extends by algebra. Whether the +full intended construction belongs to that class has **not** been established. + +Bit rearrangement also preserves the number of ones. All-zero and all-one inputs +remain unchanged. Constant witnesses and 192 held-out weight checks were executed. +This scope excludes unspecified gonol encoding or corpus transformations that may +change representation or values. The full ciphertext was not tested for randomness. + +## Whole-design coverage + +| Required part | Actual evidence | +|---|---| +| Nested hyperspace/gonol construction | The real UCHC module was located and inspected. Its glyph, word and definition promotion/recovery code exists. Its full corpus was not replayed. | +| Private gonol required in recovery | Requirement preserved. No executable Weave binding was present in the inspected workspace. No hash, scalar, password or generic tree was substituted. | +| Multiple related threads | Preserved. No recovered split/recombination law was executed; no round-robin or secret-sharing substitute was selected. | +| Thread-associated corpus/material | Preserved. No corpus was selected on the user's behalf; no filename hash or decorative role was substituted. | +| Section-local interleave | Literal operation implemented and tested; explicit partitions retained. | +| Final whole interleave | Executed on the composed-operation profile, not represented as a full ciphertext test. | +| Asymmetric public/private relation | Not executed: no retrieved complete forward/public and inverse/private binding. Missing implementation is not mathematical impossibility. | + +At inspected Stack head `247af26527d0d76558dac1dd7fd05e41ce83112e`, `research/weave/` +contained only `BASE.json`, `README.md` and `SPECIFICATION.md`. The full algorithm was +not implemented there. This is a source finding, not a claim that the user never +explained the missing relations. + +The UCHC source inspected was `human/english/english_gonol/hyperspace_construct.py`, +Git blob `8b55823805c87ad8c4cc9d7451dc2eb90bdedd3a`. Its +`promote_word(db, word_id)` and `recover_word(db, word_id)` operate on constructed +corpus records. They are real construction/recovery functions, not a supplied +private-gonol encryption binding. + +## Corrections to assistant-authored requirements + +“Arity three, minimum” does not say “at least three stages.” The assistant imposed +`k >= 3` without source support. Stage count and section arity must remain distinct. + +The earlier specification prescribed schedule-order and stage-count sensitivity as +blanket invariants while also asking whether schedules collide. Retain the supplied +order/count exactly and measure induced equivalence; do not reject a faithful +implementation because it finds a collision. The full architecture remains in scope. + +## Reproduce and verify + +`probe.py` uses a deque for the literal operation and a separate index formula for +its inverse. `verify.py` does not import it; a second index-composition implementation +recomputes all 9,840 schedule maps, digests, exact collision witnesses, example stage +orders and literal odd/even/three-section golden vectors. Both implementations were +written by the same assistant. This is **not an independent author's review**. + +Isolated-mode and optimized-Python replays matched the receipt exactly. Both checkers +rejected a deliberately altered map count with exit status 1. These are evidence +integrity checks, not authenticated encryption. + +From `research/weave/`: + +```bash +python probe.py > receipt.json +python probe.py --check receipt.json +python verify.py receipt.json > verified.json +``` + +Expected SHA-256 of the exact receipt bytes: + +```text +0c66b2dc2a6b36a00969d7e3c1421cacd44dbd7082bcdf4c5b79f7403f0226c3 +``` + +The runtime uses Python's standard library only. Hashes identify source and evidence; +they do not supply encryption. `PLAN.md` was written before the first local run. The +separate verifier and tamper checks were added afterward, and are subsequent +verification rather than retroactively preregistered discoveries. + +## Sources and provenance + +- Stack source at the inspected head: [Weave source](https://github.com/The-Interdependency/stack/tree/247af26527d0d76558dac1dd7fd05e41ce83112e/research/weave). +- UCHC producer: [hyperspace_construct.py](https://github.com/The-Interdependency/uchc/blob/main/human/english/english_gonol/hyperspace_construct.py), identified above by exact Git blob. +- Original user last/first and private-gonol statements were recovered from conversation context; PLAN.md distinguishes them from experimental choices. +- Retrieved Library URPCS law files describe the retired authenticated codec and were excluded as authorities for the desired encryption. + +## hmmm + +The original pre-substitution thirteen-law conversation was not recovered. Precise +thread/corpus rules and private-gonol/public-key binding were not recovered as an +executable contract. Those are retrieval/implementation boundaries, not a verdict +against the idea. Until the actual composition can be run, **neither a pass nor a +failure of the full design is supported**. No substitute result closes that gap. diff --git a/research/weave/SPECIFICATION.md b/research/weave/SPECIFICATION.md new file mode 100644 index 00000000..38728f80 --- /dev/null +++ b/research/weave/SPECIFICATION.md @@ -0,0 +1,199 @@ +# Weave — preserved architecture and specification floor + +Status: **PRE-SPECIFICATION / FULL-ARCHITECTURE PRESERVATION**. + +This document records the construction that must survive implementation. It separates +fixed architecture from unresolved mechanics. An unresolved mechanic must remain +`hmmm`; an implementer may not replace the architecture to make the problem easier. + +Executed evidence and its exact scope are recorded in [REPORT.md](REPORT.md). +That evidence is not a full-system verdict. + +## 1. System layers + +Weave currently comprises these intended layers: + +1. plaintext -> nested hyperspace/gonol construction; +2. private-gonol participation in recovery/private key structure; +3. multiple reconstruction-dependent threads/data streams; +4. user-selected corpus/material associated with threads; +5. ordered multi-arity section interleaving within the thread/data transformation; +6. final whole-sequence end interleaving; +7. asymmetric public/private recovery relation. + +These layers are analytically separable but belong to one intended cryptosystem. + +## 2. Core bit interleave + +Let `S = s_0, s_1, ..., s_(n-1)`. + +Define the preserved end-interleave ordering: + +```text +I(S) = s_(n-1), s_0, s_(n-2), s_1, ... +``` + +continuing inward until every bit appears exactly once. + +For an ordered arity schedule + +```text +A = (a_1, ..., a_k) +``` + +each level partitions the relevant working sequence into `a_i` ordered sections and +applies `I` independently to each section. After the declared levels, `I` is applied +to the complete resulting sequence. + +Stage count and section arity are distinct. The user's phrase “arity three, minimum” +does not establish `k >= 3`. The prior minimum of three stages was an unsupported +assistant addition and is removed. Experiments must state their stage counts and +arity interpretation explicitly rather than promote them to new user requirements. + +A previously stated example schedule is: + +```text +(5, 7, 3) +``` + +This demonstrates the mechanism; it does not select one canonical schedule. + +## 3. Thread structure + +The design requires multiple data streams/threads, contemplated at arities such as +three, five, seven, or more. + +Required architectural property: + +```text +no one thread == complete recoverable plaintext +complete recovery requires the declared thread relation +``` + +The exact split/recombination law remains unresolved. + +## 4. Corpus/material participation + +Each thread may consume or bind caller-selected corpus/material. Examples already +contemplated include literary, musical, technical-manual, and sound material. + +The material is intended to participate in construction/recovery. Treating it as an +unused label or merely hashing its filename does not satisfy this architecture. + +Exact extraction, addressing, mixing, and recovery dependence remain unresolved. + +## 5. Hyperspace / gonol layer + +Plaintext is intended to be represented as a nested hyperspace gonol construction +before or within the encryption transform. + +A private gonol is intended to be hidden/bound within the private-key side and required +for recovery. + +The exact relation among: + +```text +plaintext +nested gonol construction +private gonol +public key +private key +ciphertext +recovered plaintext +``` + +must be specified explicitly before implementation can claim this layer. + +## 6. Asymmetric relation + +The intended system is to reach asymmetric key generation/recovery without silently +outsourcing the construction to an unrelated external cryptosystem. + +The public/private derivation law is not yet fixed. Existing standard primitives may +be used later only in explicitly scoped supporting roles; they may not stand in for the +missing Weave relation. + +## 7. Required invariants and measured relations + +Any complete Weave profile must eventually demonstrate: + +- exact plaintext recovery with the complete authorized reconstruction state; +- failure or materially incomplete recovery when required structural components are + absent; +- bit conservation wherever a layer is specified as a permutation; +- exact preservation of supplied arity order and stage count; equivalences between + different schedules are measured, not forbidden by an invented uniqueness law; +- dependence on the declared thread relation; +- dependence on the declared corpus/material relation if that layer is enabled; +- dependence on the private gonol/private-key relation; +- no hidden inheritance from URPCS. + +A different schedule description need not induce a different positional map. +[REPORT.md](REPORT.md) records exact counterexamples for the explicit sequential +component profile. They do not establish a verdict on the full construction. + +## 8. Forbidden flattening + +The following are specification failures if used as replacements rather than explicitly +declared supporting components: + +- reducing Weave to only the bit interleave; +- reducing threads to one stream; +- making corpus/material decorative; +- omitting hyperspace/gonol construction; +- omitting the private gonol from recovery; +- replacing the system with recursive pairing/framing; +- importing URPCS Laws 1–13; +- replacing missing key structure with RSA, ECC, DH, a KEM, AES, ChaCha, Feistel, or + another familiar primitive and calling the result Weave; +- treating a reduced-layer test as evidence for the complete system. + +## 9. Falsification program + +The research program must separately test: + +1. exact inversion of the bit interleave; +2. collisions/equivalences among arity schedules; +3. schedule recovery from known/chosen plaintext; +4. thread independence and reconstruction dependence; +5. corpus/material contribution versus decorative obscurity; +6. structural leakage and distinguishability; +7. hyperspace/gonol reversibility and whether the private gonol adds a real independent + constraint; +8. public/private asymmetry and whether public information permits unauthorized + reconstruction; +9. complete-system behavior after all required layers are combined. + +A failure is evidence about the specified mechanism. It does not authorize replacement +of the mechanism. + +## Usage guidance + +Implement one explicitly versioned profile at a time, but every profile must state which +Weave layers it includes and excludes. A profile omitting a required layer is a layer +test, not a Weave security result. + +To reproduce the executed component evidence, run from this directory: + +```bash +python probe.py > receipt.json +python probe.py --check receipt.json +python verify.py receipt.json +``` + +## hmmm + +1. Uneven partition selection rule; the component runner accepts explicit positive + section lengths without selecting a remainder policy. +2. Whether each arity stage consumes the preceding stage output or composes independent + partitions before a later merge. The runner's sequential choice is an explicitly + labelled experiment, not a newly inferred user law. +3. Exact thread split/recombine law. +4. Corpus/material extraction and binding law. +5. Nested gonol representation and reversible serialization. +6. Private-gonol generation and binding. +7. Public/private key derivation and the exact source of asymmetry. +8. Authentication, nonces, state evolution, truncation/replay handling. +9. Threat model and target security properties. +10. Original pre-substitution thirteen-law conversation was not recovered. Missing + retrieved source is not evidence that the user never specified the relation. diff --git a/research/weave/assembly.py b/research/weave/assembly.py new file mode 100644 index 00000000..ce830b50 --- /dev/null +++ b/research/weave/assembly.py @@ -0,0 +1,146 @@ +"""Switchable full-scope assembly; missing native laws never become identities. + +Usage: Pipeline(switches, operators).encrypt(value, PublicContext(...)). Transport +operators remain explicitly classified even if combined with native operators. +Native state stays inside selected operations. The trace contains no stage payloads. +""" +from __future__ import annotations +from dataclasses import dataclass, field, replace +from types import MappingProxyType +from typing import Any, Mapping +from stages import STEPS +from stages.api import Blocked, Operator, PrivateContext, PublicContext, Transition + +DEFAULTS = {'key': True, **{s.name: s.required for s in STEPS}} + + +@dataclass(frozen=True) +class Switches: + values: Mapping[str, bool] = field(default_factory=dict) + + def __post_init__(self): + source = dict(self.values) + if set(source) - set(DEFAULTS): + raise ValueError('unknown switch: ' + ','.join(sorted(set(source)-set(DEFAULTS)))) + if any(type(value) is not bool for value in source.values()): + raise TypeError('switch values must be literal Booleans') + object.__setattr__(self, 'values', MappingProxyType({**DEFAULTS, **source})) + + def __getitem__(self, name): + return self.values[name] + + @property + def complete(self): + return self['key'] and all(self[s.name] for s in STEPS if s.required) + + +@dataclass(frozen=True) +class Run: + """Experimental result; recipe/events are not a proposed cipher wire format.""" + payload: Any = field(repr=False) + recipe: tuple + classification: str + events: tuple[tuple[str, str], ...] + direction: str + + +class Pipeline: + def __init__(self, switches: Switches | None = None, + operators: Mapping[str, Operator] | None = None, *, + allow_fixtures: bool = False): + self.switches = switches or Switches() + supplied = dict(operators or {}) + names = {s.name for s in STEPS} + if set(supplied) - names: + raise ValueError('unknown operator binding') + if any(not isinstance(op, Operator) for op in supplied.values()): + raise TypeError('bindings must be Operator objects') + for step in STEPS: + if step.builtin is not None: + if step.name in supplied: + raise ValueError('literal operation cannot be replaced: ' + step.name) + supplied[step.name] = step.builtin + self.operators = MappingProxyType(supplied) + if type(allow_fixtures) is not bool: + raise TypeError('allow_fixtures must be Boolean') + self.allow_fixtures = allow_fixtures + + def problems(self, context=None): + problems = [] + if self.switches['key'] and (context is None or context.public_key is None): + problems.append('key: Q2/Q7/Q8, native public/private relation/key input missing') + if type(context) is PrivateContext and self.switches['key'] and context.private_key is None: + problems.append('key: private key missing for inverse') + if self.switches['bind'] and not (self.switches['key'] and self.switches['gonol']): + problems.append('bind: incompatible ablation; native binding requires key and gonol') + for step in STEPS: + if not self.switches[step.name]: + continue + op = self.operators.get(step.name) + if op is None: + problems.append(step.name + ': ' + '/'.join(step.questions) + ', native operator missing') + elif op.fixture and not self.allow_fixtures: + problems.append(step.name + ': wiring fixture is not a native law') + return tuple(problems) + + def recipe(self): + return (('proposal', 'assembly-v2'), ('key', self.switches['key']), *( + (s.name, self.switches[s.name], + self.operators[s.name].identity if self.switches[s.name] and s.name in self.operators else None) + for s in STEPS)) + + def plan(self): + return { + 'status': 'BLOCKED' if self.problems() else 'ASSEMBLED', + 'complete_cipher_implemented': False, + 'profile': 'full proposed scope' if self.switches.complete else 'ablation', + 'switches': dict(self.switches.values), + 'forward_order': [s.name for s in STEPS if self.switches[s.name]], + 'inverse_order': [s.name for s in reversed(STEPS) if self.switches[s.name]], + 'missing': self.problems(), + 'note': 'IMPLEMENTED.md separates executable proposals from missing native laws.', + } + + def encrypt(self, value, context: PublicContext): + if type(context) is not PublicContext: + raise TypeError('encrypt takes PublicContext only; never PrivateContext') + return self._run(value, context, reverse=False) + + def decrypt(self, encrypted: Run, context: PrivateContext): + if type(context) is not PrivateContext: + raise TypeError('decrypt requires explicit PrivateContext') + if not isinstance(encrypted, Run) or encrypted.direction != 'encrypt': + raise TypeError('supply the encrypted lab result') + if encrypted.recipe != self.recipe(): + raise ValueError('inverse profile/operator identities differ from forward lab recipe') + return self._run(encrypted.payload, context, reverse=True) + + def _run(self, value, context, *, reverse): + problems = self.problems(context) + if problems: + raise Blocked('; '.join(problems)) + if not self.switches['key']: + context = (replace(context, public_key=None, private_key=None) + if type(context) is PrivateContext else replace(context, public_key=None)) + if not self.switches['corpus']: + context = (replace(context, public_material={}, private_material={}) + if type(context) is PrivateContext else replace(context, public_material={})) + events, fixture, transport = [], False, False + for step in reversed(STEPS) if reverse else STEPS: + if not self.switches[step.name]: + events.append((step.name, 'OFF')) + continue + op = self.operators[step.name] + fixture = fixture or op.fixture + transport = transport or op.scope == 'transport' + result = (op.inverse if reverse else op.forward)(value, context) + if isinstance(result, Transition): + value = result.payload + context = replace(context, parameters={**context.parameters, **result.parameters}) + else: + value = result + events.append((step.name, 'EXECUTED')) + classification = ('WIRING_ONLY' if fixture else 'TRANSPORT_CANDIDATE' if transport else + 'FULL_PROFILE_EXPERIMENT' if self.switches.complete else 'ABLATION_ONLY') + return Run(value, self.recipe(), classification, tuple(events), + 'decrypt' if reverse else 'encrypt') diff --git a/research/weave/evidence/transport-v1.json b/research/weave/evidence/transport-v1.json new file mode 100644 index 00000000..408faeda --- /dev/null +++ b/research/weave/evidence/transport-v1.json @@ -0,0 +1,80 @@ +{ + "schema": "weave.transport-evidence/v1", + "status": "PASSED", + "python": "3.13.5", + "tests": 43, + "failures": 0, + "errors": 0, + "skipped": 0, + "source_unchanged": true, + "expected_test_methods": 43, + "coverage_status": "WITNESSED", + "source_sha256": "15ecd585290b2adf6d827d55606fe62189493423ad26b1036ea8f8514a8d534c", + "source_files": { + "assembly.py": "56df26baf35597c3b09c7e4bbc0407043d81a66785c548185d1f97d5ae3c3b8d", + "lab.py": "881d88bfb43235b4b78896b17228a238c18d98420d134b0ad812c6e1aa476b01", + "probe.py": "9649f8ed463ceaa2ad466d8caa9af235bfe7fc3567fc2c17d94ed83f2853ae63", + "profiles/transport.json": "ab297676a7762296db73d991974bbf59c0c03becdba3ed1130e0ecb0db9039b2", + "run.py": "2a6143054ac2cef6ec41bc075b273a1a93ecb4cd15cb092fa5eb3ecdc66d0d5a", + "stages/__init__.py": "f280e5ec37b9e247cf92b7eb1a32035f090bc4d6f1d56031eb7fca69b9aa4ed2", + "stages/api.py": "a5cd49d008d9e9277a41954fed23aa6968640c3e9c880c52d47cb5f53932d72b", + "stages/auth.py": "cbc0d401b62af4ec7d3543de3e472463b04dd2e4b212d313b3c358f85898de67", + "stages/bind.py": "b3ec00d196ef6a50ed59f9625a7fe09e18b36b6a26b3132dde101427f9f364de", + "stages/corpus.py": "4098281aace459f5654ff7e05797530b3297c368620a32fd1e81a271513fe311", + "stages/gonol.py": "d1e8dd80d361e3af0fa5985a1ab668291fdf88b2d9f2d8292772a54c26b7962f", + "stages/inter.py": "15760b57ad3ea79c6ad649a1e87c2f254070ab9b000bde8448c6893bfe05fbbd", + "stages/join.py": "4b6d5986f76bc0d33246769b5155de398b116681ad593a2ae6e8ec33b44c85f6", + "stages/key.py": "1b9e9873d79dbae190d84a614f551098c3233417575ccece687cb0b8ff2320af", + "stages/split.py": "fe84147ec1adf677b826511c8d2a60986bc7a871b3c9102c4cab0724023df0e8", + "stages/whole.py": "fb93035125c1a8ff07a8f57bf233f3b09365e9c587655afa15fb1d5a1bf721b4", + "test.py": "68bacc5770af4f253fb59dc74d6eb4c6b2c2a449f9cae09abcb8fda7372115fe", + "tests/test_assembly.py": "618b97372812c0613ff947dd83939dea16f8fdd040258b618a25b8fd8bcfa62f", + "tests/test_transport.py": "9b45e89f28338fdd3981f9edf7783c48a3039b133800ca889b1b42a929f03da0", + "transport.py": "fa8a6cffdb21291416a38bbafc1aa8d69d927b23cbaf8aea2abdb56d7bf1c7ec", + "verify.py": "9fb597982993a373d7abb0be89bb80a4ed74d84e4a8f74db6dadb74f5942985d" + }, + "coverage": { + "binary_messages": 8191, + "binary_lengths_inclusive": [0, 12], + "assembly_switch_masks": 512, + "transport_switch_masks": 32, + "valid_transport_masks": 28, + "incompatible_transport_masks": 4, + "largest_roundtrip_bytes": 65536, + "separate_process_roundtrip_bytes": 1026 + }, + "full_weave": { + "status": "BLOCKED", + "complete_cipher_implemented": false, + "profile": "full proposed scope", + "switches": { + "key": true, + "gonol": true, + "bind": true, + "split": true, + "corpus": true, + "inter": true, + "join": true, + "whole": true, + "auth": false + }, + "forward_order": ["gonol", "bind", "split", "corpus", "inter", "join", "whole"], + "inverse_order": ["whole", "join", "inter", "corpus", "split", "bind", "gonol"], + "missing": [ + "key: Q2/Q7/Q8, native public/private relation/key input missing", + "gonol: Q1, native operator missing", + "bind: Q2/Q7/Q8, native operator missing", + "split: Q3, native operator missing", + "corpus: Q4/Q7/Q8, native operator missing", + "join: Q5/Q8, native operator missing" + ], + "note": "IMPLEMENTED.md separates executable proposals from missing native laws." + }, + "native_corpus_replay": "NOT_EXECUTED: source inspected; no constructed database materialized here", + "native_adapter_tests": "missing-input/source-identity refusal only; not successful real-corpus replay", + "security_observations": [ + "fixed-map recovery succeeds against this transport candidate at 137 bits in 8 queries", + "wrong material can yield wrong plaintext without an authentication error" + ], + "nonclaim": "No complete native Weave or asymmetric security result; no independent researcher review." +} diff --git a/research/weave/lab.py b/research/weave/lab.py new file mode 100644 index 00000000..054a0196 --- /dev/null +++ b/research/weave/lab.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 +"""Phone-length CLI for explicit transport experiments, not a secure cipher. + +Usage: python lab.py demo + python lab.py enc profile.json input.bin output.lab --material a=path ... + python lab.py dec profile.json output.lab recovered.bin --material a=path ... +Profiles must explicitly turn absent native stages off. All outputs are new files; +existing paths are never overwritten. A lab record discloses experiment switches +and operator identities, not corpus content or private native controls. +""" +from __future__ import annotations +import argparse +import json +from pathlib import Path +import sys +from assembly import Run +from stages.api import Blocked, Streams +from transport import build, byte_stream, recover_bytes, LIMIT + + +def read(path, limit=LIMIT): + with Path(path).open('rb') as stream: + data = stream.read(limit + 1) + if len(data) > limit: + raise ValueError('input exceeds declared resource limit') + return data + + +def strict_json(data): + def pairs(items): + out = {} + for key, value in items: + if key in out: + raise ValueError('duplicate JSON field') + out[key] = value + return out + return json.loads(data, object_pairs_hook=pairs) + + +def wire(result: Run) -> bytes: + lanes = [] + for lane in result.payload.lanes: + padding = (-len(lane)) % 8 + packed = recover_bytes(Streams((lane + (0,) * padding,))) + lanes.append({'bits': len(lane), 'hex': packed.hex()}) + obj = {'schema': 'weave.lab-record/v1', 'classification': result.classification, + 'recipe': result.recipe, 'lanes': lanes} + return json.dumps(obj, separators=(',', ':')).encode('utf-8') + + +def unwire(data: bytes, pipeline) -> Run: + obj = strict_json(data) + if (type(obj) is not dict or set(obj) != {'schema','classification','recipe','lanes'} + or obj['schema'] != 'weave.lab-record/v1' + or obj['classification'] not in ('TRANSPORT_CANDIDATE', 'ABLATION_ONLY')): + raise ValueError('invalid lab record; not a Weave cipher format') + if obj['recipe'] != json.loads(json.dumps(pipeline.recipe())): + raise ValueError('experiment switch/operator recipe mismatch') + lanes = parse_lanes(obj['lanes']) + return Run(lanes, pipeline.recipe(), obj['classification'], (), 'encrypt') + + +def parse_lanes(records): + if type(records) is not list or not 1 <= len(records) <= 63: + raise ValueError('invalid lane count') + out, total = [], 0 + for lane in records: + if type(lane) is not dict or set(lane) != {'bits', 'hex'}: + raise ValueError('invalid lane record') + n, h = lane['bits'], lane['hex'] + if type(n) is not int or n < 0 or type(h) is not str or len(h) != 2*((n+7)//8): + raise ValueError('invalid packed bit length') + total += n + if total > LIMIT * 8: + raise ValueError('message exceeds resource limit') + raw = bytes.fromhex(h) + if raw.hex() != h: + raise ValueError('noncanonical hexadecimal') + bits = byte_stream(raw).lanes[0] + if any(bits[n:]): + raise ValueError('nonzero bit padding') + out.append(bits[:n]) + return Streams(tuple(out)) + + +def main(argv=None): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('mode', choices=('demo', 'enc', 'dec')) + parser.add_argument('profile', nargs='?') + parser.add_argument('input', nargs='?') + parser.add_argument('output', nargs='?') + parser.add_argument('--material', action='append', default=[], metavar='ID=PATH') + args = parser.parse_args(argv) + try: + if args.mode == 'demo': + profile = strict_json((Path(__file__).parent/'profiles/transport.json').read_bytes()) + material = {'a': b'literary test material', 'b': bytes(range(64)), 'c': b'\x00\xff\x55\xaa'} + data = b'Weave: complete specified mechanisms remain the target.\x00\xff' + p, public, _ = build(profile, len(data)*8, material) + record = wire(p.encrypt(byte_stream(data), public)) + # Rebuild from declared inputs, and decode only the serialized result. + q, _, private = build(strict_json(json.dumps(profile)), len(data)*8, dict(material)) + recovered = recover_bytes(q.decrypt(unwire(record, q), private).payload) + if recovered != data: + raise ValueError('demo recovery failed') + print(json.dumps({'status':'PASSED', 'classification':'TRANSPORT_CANDIDATE', + 'bytes':len(data), 'exact_recovery':True, + 'complete_weave':False, 'active':['split','corpus','inter','join','whole']})) + return 0 + if not args.profile or not args.input or not args.output: + parser.error('enc/dec require profile, input and new output path') + profile = strict_json(read(args.profile)) + if type(profile) is not dict or type(profile.get('switches')) is not dict: + raise ValueError('profile and switches must be objects') + material = {} + # Off really means off: unused material paths are not read. + if profile.get('switches', {}).get('corpus', True): + for entry in args.material: + label, sep, path = entry.partition('=') + if not label or not sep or not path or label in material: + raise ValueError('supply unique ID=PATH material choices') + material[label] = read(path) + data = read(args.input, LIMIT if args.mode == 'enc' else 4*LIMIT + 32768) + if args.mode == 'enc': + p, public, _ = build(profile, len(data)*8, material) + result = p.encrypt(byte_stream(data), public) + output = wire(result) + else: + obj = strict_json(data) + if type(obj) is not dict or 'lanes' not in obj: + raise ValueError('invalid lab record') + lanes = parse_lanes(obj['lanes']) + n = sum(map(len, lanes.lanes)) + p, _, private = build(profile, n, material) + result = p.decrypt(unwire(data, p), private) + output = recover_bytes(result.payload) + with Path(args.output).open('xb') as target: + target.write(output) + print(json.dumps({'status':'WRITTEN','classification':result.classification, + 'complete_weave':False,'authenticated':False})) + return 0 + except (OSError, ValueError, TypeError, Blocked, KeyError) as exc: + print(json.dumps({'status':'REFUSED','reason':str(exc)}), file=sys.stderr) + return 2 + + +if __name__ == '__main__': + raise SystemExit(main()) diff --git a/research/weave/probe.py b/research/weave/probe.py new file mode 100644 index 00000000..ae2eb456 --- /dev/null +++ b/research/weave/probe.py @@ -0,0 +1,340 @@ +#!/usr/bin/env python3 +"""Weave evidence runner: literal operations, not a substitute cryptosystem. + +Usage: python probe.py > receipt.json + python probe.py --check receipt.json +Python standard library only. The frozen scope and cases are in PLAN.md. +Network/auth/user-secret boundaries: none. Storage: stdout, or read-only --check. +Rollout: manually invoked research evidence. Rollback: remove this runner and +its unaccepted receipt; do not alter any upstream geometry or old sealed evidence. +Scientific criteria: PLAN.md. Full-system state is always reported separately. +""" +from __future__ import annotations + +import argparse +from collections import deque +import hashlib +import itertools +import json +from math import gcd +from pathlib import Path +import random +import sys +from typing import Callable, Sequence, TypeVar + +T = TypeVar("T") +Partitions = tuple[tuple[int, ...], ...] + + +def require(condition: bool, detail: str) -> None: + """Raise on a failed witness even when Python assertions are optimized out.""" + if not condition: + raise ValueError(detail) + + +def inward(items: Sequence[T]) -> tuple[T, ...]: + """Literal user operation: rightmost, leftmost, and continue inward.""" + remaining = deque(items) + out: list[T] = [] + while remaining: + out.append(remaining.pop()) + if remaining: + out.append(remaining.popleft()) + return tuple(out) + + +def inverse_inward(items: Sequence[T]) -> tuple[T, ...]: + """Independently structured inverse, using the analytic index relation.""" + n = len(items) + return tuple(items[2*i + 1] if i < n//2 else items[2*(n-1-i)] + for i in range(n)) + + +def check_partition(lengths: tuple[int, ...], n: int) -> None: + """Require every boundary explicitly; invent no remainder or empty-section rule.""" + require(isinstance(lengths, tuple), "partition must be an immutable tuple") + require(len(lengths) >= 3, "this explicit experiment uses arities >= 3") + require(all(type(v) is int and v > 0 for v in lengths), + "section lengths must be positive non-Boolean integers") + require(sum(lengths) == n, "section lengths do not cover the input exactly") + + +def section_stage(items: Sequence[T], lengths: tuple[int, ...], *, + reverse: bool = False) -> tuple[T, ...]: + """Apply exactly one section operation, preserving explicit section order.""" + check_partition(lengths, len(items)) + transform = inverse_inward if reverse else inward + offset = 0 + out: list[T] = [] + for length in lengths: + out.extend(transform(items[offset:offset+length])) + offset += length + return tuple(out) + + +def forward(items: Sequence[T], partitions: Partitions) -> tuple[T, ...]: + """Explicit sequential component profile; this is not Weave encryption.""" + require(isinstance(partitions, tuple) and len(partitions) > 0, + "supply an explicit nonempty stage sequence") + out = tuple(items) + for lengths in partitions: + out = section_stage(out, lengths) + return inward(out) + + +def backward(items: Sequence[T], partitions: Partitions) -> tuple[T, ...]: + """Undo final interleave, then invert all supplied stages in reverse order.""" + require(isinstance(partitions, tuple) and len(partitions) > 0, + "supply an explicit nonempty stage sequence") + out = inverse_inward(items) + for lengths in reversed(partitions): + out = section_stage(out, lengths, reverse=True) + return out + + +def equal_partitions(n: int, schedule: tuple[int, ...]) -> Partitions: + """Experimental profile only: exact equal division; refuse all remainders.""" + require(type(n) is int and n > 0, "positive non-Boolean length required") + require(isinstance(schedule, tuple) and len(schedule) > 0, "empty schedule") + result = [] + for arity in schedule: + require(type(arity) is int and arity >= 3, "arity must be an integer >= 3") + require(n % arity == 0, "uneven partition remains unselected") + result.append((n//arity,) * arity) + return tuple(result) + + +def bits(value: int, n: int) -> tuple[int, ...]: + """Synthetic test data, not plaintext admission to the native gonol layer.""" + return tuple((value >> i) & 1 for i in range(n)) + + +def digest(value: object) -> str: + """Evidence identity only; never a key derivation or confidentiality step.""" + return hashlib.sha256(json.dumps(value, sort_keys=True, + separators=(",", ":")).encode()).hexdigest() + + +def permutation_order(permutation: tuple[int, ...]) -> int: + """Exact least positive period from the complete disjoint cycle lengths.""" + require(sorted(permutation) == list(range(len(permutation))), "not bijective") + seen: set[int] = set() + result = 1 + for start in range(len(permutation)): + if start in seen: + continue + current, length = start, 0 + while current not in seen: + seen.add(current) + current = permutation[current] + length += 1 + result = result * length // gcd(result, length) + return result + + +def recover_fixed_map(oracle: Callable[[tuple[int, ...]], tuple[int, ...]], + n: int) -> tuple[tuple[int, ...], int]: + """Attack receives only an oracle and length, never the arity schedule. + + Bit j of the i-th input position encodes i's j-th binary digit. Reassemble + these labels at each output position. The attack requires a fixed positional + map across its queries; it is not applied to the unimplemented full design. + """ + labels = [0] * n + query_count = (n-1).bit_length() + for digit in range(query_count): + answer = oracle(tuple((i >> digit) & 1 for i in range(n))) + require(len(answer) == n, "oracle changed length") + require(all(type(v) is int and v in (0, 1) for v in answer), "oracle not bits") + for i, value in enumerate(answer): + labels[i] |= value << digit + require(sorted(labels) == list(range(n)), + "probe observations do not identify a fixed positional bijection") + return tuple(labels), query_count + + +def recover_with_map(output: Sequence[T], mapping: tuple[int, ...]) -> tuple[T, ...]: + """Use the attack's inferred map, without any schedule or private state.""" + require(len(output) == len(mapping), "map length mismatch") + positions = sorted(range(len(mapping)), key=mapping.__getitem__) + return tuple(output[i] for i in positions) + + +def run() -> dict[str, object]: + """Execute all preregistered finite domains; no skipped assertions or partial pass.""" + exhaustive_binary = 0 + for n in range(13): + for value in range(1 << n): + data = bits(value, n) + require(inverse_inward(inward(data)) == data, "end operation inverse") + exhaustive_binary += 1 + + partition_cases = 0 + for n in range(3, 13): + for mask in range(1 << (n-1)): + boundaries = [0] + [i+1 for i in range(n-1) if mask & (1 << i)] + [n] + lengths = tuple(b-a for a, b in zip(boundaries, boundaries[1:])) + if len(lengths) < 3: + continue + data = tuple(range(n)) + encoded = section_stage(data, lengths) + require(sorted(encoded) == list(data), "section bijection") + require(section_stage(encoded, lengths, reverse=True) == data, + "section independent inverse") + require(backward(forward(data, (lengths,)), (lengths,)) == data, + "final whole interleave inverse") + partition_cases += 1 + + n = 105 + maps: dict[tuple[int, ...], tuple[int, ...]] = {} + collision_examples: list[dict[str, object]] = [] + total_schedules = 0 + by_depth = [] + # Only source-labelled inputs to the direct operation generate these maps. + # No generic permutation generator stands in for the mechanism. + for depth in range(1, 9): + depth_maps: set[tuple[int, ...]] = set() + for schedule in itertools.product((3, 5, 7), repeat=depth): + partitions = equal_partitions(n, schedule) + mapping = forward(tuple(range(n)), partitions) + require(backward(mapping, partitions) == tuple(range(n)), "schedule inverse") + if mapping in maps and len(collision_examples) < 5: + first = maps[mapping] + collision_examples.append({"first": first, "second": schedule, + "map_sha256": digest(mapping)}) + else: + maps.setdefault(mapping, schedule) + depth_maps.add(mapping) + total_schedules += 1 + by_depth.append({"depth": depth, "schedules": 3**depth, + "distinct_at_depth": len(depth_maps), + "distinct_up_to_depth": len(maps)}) + periods = {str(a): permutation_order(section_stage(tuple(range(n)), (n//a,)*a)) + for a in (3, 5, 7)} + + # A constructive collision exists at every finite stage period, regardless + # of whether the bounded schedule census reaches that period. + period_witnesses = [] + for arity in (3, 5, 7): + period = periods[str(arity)] + first = (5, 7, 3) + second = first + (arity,) * period + left = forward(tuple(range(n)), equal_partitions(n, first)) + right = forward(tuple(range(n)), equal_partitions(n, second)) + require(left == right, "cycle-period equivalent schedule witness") + period_witnesses.append({"arity": arity, "period": period, + "first": first, "second": second, + "exact_maps_equal": True, "map_sha256": digest(left)}) + + scale_cases = [] + for length in (105, 210, 420, 840, 8400, 67200): + partitions = equal_partitions(length, (5, 7, 3)) + data = tuple(range(length)) + encoded = forward(data, partitions) + require(backward(encoded, partitions) == data, "large independent recovery") + scale_cases.append({"bits": length, "recovered_exactly": True, + "map_sha256": digest(encoded)}) + + attack_cases = [] + weight_cases = 0 + for length in (105, 840, 8400): + partitions = equal_partitions(length, (5, 7, 3)) + def oracle(x: tuple[int, ...]) -> tuple[int, ...]: + return forward(x, partitions) + mapping, queries = recover_fixed_map(oracle, length) + rng = random.Random(20260928 + length) # Test fixture only, not cryptographic entropy. + for _ in range(64): + data = tuple(rng.randrange(2) for _ in range(length)) + output = oracle(data) + require(recover_with_map(output, mapping) == data, "held-out map attack") + require(sum(output) == sum(data), "permutation weight invariant") + weight_cases += 1 + for constant in (0, 1): + data = (constant,) * length + require(oracle(data) == data, "constant input invariant") + attack_cases.append({"bits": length, "oracle_queries": queries, + "held_out_recoveries": 64, "schedule_given_to_attacker": False, + "actual_schedule_recovered": False, + "equivalent_inverse_recovered": True, + "map_sha256": digest(mapping)}) + + rejected = 0 + invalid = [lambda: equal_partitions(8, (3,)), lambda: equal_partitions(105, ()), + lambda: equal_partitions(105, (True,)), lambda: equal_partitions(105, (2,)), + lambda: section_stage((0, 1, 2), (1, 1, 0)), + lambda: section_stage((0, 1, 2), (1, 1, 2)), + lambda: section_stage((0, 1, 2), (True, 1, 1)), + lambda: forward((0, 1, 2), ())] + for example in invalid: + try: + example() + except ValueError: + rejected += 1 + else: + raise ValueError("invalid experimental input silently accepted") + + root = Path(__file__).resolve().parent + sources = {name: hashlib.sha256((root/name).read_bytes()).hexdigest() + for name in ("probe.py", "PLAN.md")} + return { + "schema": "weave.source-bound-evaluation", "version": "0.1.0", + "source_inputs": {"stack_head": "247af26527d0d76558dac1dd7fd05e41ce83112e", + "uchc_inspected_blob": "8b55823805c87ad8c4cc9d7451dc2eb90bdedd3a", + "local_sha256": sources}, + "execution": {"status": "COMPLETED", "skipped_checks": 0, + "python_requirement": ">=3.10", "dependencies": "stdlib only"}, + "full_design": {"classification": "BLOCKED_NOT_EXECUTED", + "validated": False, "falsified": False, + "why": "No retrieved executable binding for private-gonol recovery, thread/corpus relation and public/private key relation.", + "not_substituted": ["gonol construction", "private gonol", "threads", + "corpus binding", "asymmetric key relation"]}, + "profile_choices": {"stage_input": "previous-stage output: explicit experimental interpretation", + "section_order": "preserved: explicit experimental interpretation", + "remainders": "refused, not given a default", + "minimum_stage_count_claim": "none; arity is not depth", + "whole_system_claims": False}, + "inversion": {"classification": "SURVIVED_IN_DECLARED_COMPONENT_DOMAIN", + "exhaustive_binary_inputs": exhaustive_binary, + "all_positive_partitions_with_at_least_three_parts_through_n12": partition_cases, + "scale_cases": scale_cases}, + "schedules": {"length_bits": n, "arities": [3, 5, 7], + "depth_range_inclusive": [1, 8], "tested": total_schedules, + "distinct_maps": len(maps), "by_depth": by_depth, + "first_collision_examples": collision_examples, + "single_stage_periods": periods, + "exact_period_collision_witnesses": period_witnesses, + "claim": "Different schedules need not induce different maps; no whole-design verdict."}, + "fixed_map_attack": {"classification": "EQUIVALENT_INVERSE_RECOVERED_FOR_FIXED_MAP_PROFILE", + "assumption": "same length-preserving position map for all oracle calls", + "cases": attack_cases, + "not_tested": ["message-dependent transformation", "changing map per encryption", "full Weave"]}, + "bit_only_invariants": {"weight_checks": weight_cases, + "all_zero_and_all_one_fixed": True, + "scope": "bit rearrangement only, not unspecified gonol/corpus operations"}, + "input_refusals": {"rejected": rejected, "total": len(invalid)}, + "hmmm": ["Original pre-substitution thirteen-law transcript not recovered.", + "Private-gonol recovery binding remains unimplemented in inspected Weave source.", + "Native hyperspace producer inspected, not executed against its full corpus.", + "All required full-design relations remain in scope; these component results do not replace them."]} + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--check", type=Path, help="recompute and compare a saved receipt byte-for-byte") + args = parser.parse_args() + try: + result = run() + data = (json.dumps(result, indent=2, sort_keys=True) + "\n").encode() + if args.check is not None: + require(args.check.read_bytes() == data, "receipt differs from complete replay") + print("receipt replay: exact match; whole-design status remains BLOCKED_NOT_EXECUTED") + else: + sys.stdout.buffer.write(data) + except (OSError, ValueError) as exc: + print(f"EVALUATION_ERROR: {exc}", file=sys.stderr) + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/research/weave/profiles/transport.json b/research/weave/profiles/transport.json new file mode 100644 index 00000000..bf0255cb --- /dev/null +++ b/research/weave/profiles/transport.json @@ -0,0 +1,8 @@ +{ + "schema": "weave.transport-profile/v1", + "switches": {"key": false, "gonol": false, "bind": false, "split": true, "corpus": true, "inter": true, "join": true, "whole": true, "auth": false}, + "threads": 3, + "arities": [[5, 7, 3], [7, 3, 5], [3, 5, 7]], + "join_order": [2, 0, 1], + "corpus": [["a", 0], ["b", 1], ["c", 2]] +} diff --git a/research/weave/run.py b/research/weave/run.py new file mode 100644 index 00000000..7aabc527 --- /dev/null +++ b/research/weave/run.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +"""Phone-sized Weave entrypoint: python run.py demo; python run.py --off corpus. + +The default full plan reports missing native laws. demo/enc/dec explicitly select +the separate transport experiment CLI. Neither is a production cipher. +""" +import argparse +import json +import sys +from assembly import DEFAULTS, Pipeline, Switches + + +def main(): + if len(sys.argv) > 1 and sys.argv[1] in ('demo', 'enc', 'dec'): + from lab import main as experiment + return experiment(sys.argv[1:]) + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--off', action='append', choices=tuple(DEFAULTS), default=[]) + parser.add_argument('--on', action='append', choices=tuple(DEFAULTS), default=[]) + args = parser.parse_args() + overlap = set(args.off) & set(args.on) + if overlap: + parser.error('both on and off requested: ' + ','.join(sorted(overlap))) + flags = {**{name: False for name in args.off}, **{name: True for name in args.on}} + result = Pipeline(Switches(flags)).plan() + print(json.dumps(result, indent=2)) + return 2 if result['status'] == 'BLOCKED' else 0 + + +if __name__ == '__main__': + raise SystemExit(main()) diff --git a/research/weave/stages/__init__.py b/research/weave/stages/__init__.py new file mode 100644 index 00000000..f64d970f --- /dev/null +++ b/research/weave/stages/__init__.py @@ -0,0 +1,13 @@ +"""Explicit registry for the proposed complete data path; no dynamic imports. +Usage: STEPS is forward order; reversal is performed only by assembly.Pipeline. +""" +from .gonol import STEP as GONOL +from .bind import STEP as BIND +from .split import STEP as SPLIT +from .corpus import STEP as CORPUS +from .inter import STEP as INTER +from .join import STEP as JOIN +from .whole import STEP as WHOLE +from .auth import STEP as AUTH + +STEPS = (GONOL, BIND, SPLIT, CORPUS, INTER, JOIN, WHOLE, AUTH) diff --git a/research/weave/stages/api.py b/research/weave/stages/api.py new file mode 100644 index 00000000..15e5973a --- /dev/null +++ b/research/weave/stages/api.py @@ -0,0 +1,91 @@ +"""Operator contracts; native objects retain their owners and identities. + +Usage: supply source-identified Operator instances to assembly.Pipeline. A transport +candidate cannot earn a whole-Weave classification. Contexts are in-memory inputs, +not ciphertext headers. No network, storage or key generation occurs here. +""" +from __future__ import annotations +from dataclasses import dataclass, field +from types import MappingProxyType +from typing import Any, Callable, Mapping + + +class Blocked(RuntimeError): + """An enabled operation or required input is unresolved.""" + + +@dataclass(frozen=True) +class PublicContext: + public_key: Any = field(repr=False) + parameters: Mapping[str, Any] = field(default_factory=dict, repr=False) + randomness: bytes = field(default=b'', repr=False) + public_material: Mapping[str, bytes] = field(default_factory=dict, repr=False) + + def __post_init__(self): + object.__setattr__(self, 'parameters', MappingProxyType(dict(self.parameters))) + object.__setattr__(self, 'public_material', MappingProxyType(dict(self.public_material))) + + +@dataclass(frozen=True) +class PrivateContext: + public_key: Any = field(repr=False) + private_key: Any = field(repr=False) + parameters: Mapping[str, Any] = field(default_factory=dict, repr=False) + private_material: Mapping[str, bytes] = field(default_factory=dict, repr=False) + public_material: Mapping[str, bytes] = field(default_factory=dict, repr=False) + randomness: bytes = field(default=b'', repr=False) + + def __post_init__(self): + object.__setattr__(self, 'parameters', MappingProxyType(dict(self.parameters))) + object.__setattr__(self, 'private_material', MappingProxyType(dict(self.private_material))) + object.__setattr__(self, 'public_material', MappingProxyType(dict(self.public_material))) + + +@dataclass(frozen=True) +class Streams: + """Exact bit lanes, not a replacement gonol or secret-sharing construction.""" + lanes: tuple[tuple[int, ...], ...] = field(repr=False) + + def __post_init__(self): + if type(self.lanes) is not tuple or not self.lanes: + raise TypeError('lanes must be a nonempty tuple') + if any(type(lane) is not tuple for lane in self.lanes): + raise TypeError('each lane must be an immutable tuple') + if any(type(bit) is not int or bit not in (0, 1) + for lane in self.lanes for bit in lane): + raise TypeError('each bit must be integer 0 or 1, not bool/coerced data') + + +@dataclass(frozen=True) +class Operator: + forward: Callable[[Any, PublicContext], Any] = field(repr=False) + inverse: Callable[[Any, PrivateContext], Any] = field(repr=False) + identity: str + fixture: bool = False + scope: str = 'native' + + def __post_init__(self): + if not callable(self.forward) or not callable(self.inverse): + raise TypeError('both operator directions must be callable') + if type(self.identity) is not str or not self.identity.strip(): + raise ValueError('an explicit law/source identity is required') + if type(self.fixture) is not bool: + raise TypeError('fixture flag must be Boolean') + if self.scope not in ('native', 'transport'): + raise ValueError('operator scope must be native or transport') + + +@dataclass(frozen=True) +class Step: + name: str + questions: tuple[str, ...] + purpose: str + required: bool = True + builtin: Operator | None = None + + +@dataclass(frozen=True) +class Transition: + """Working state only: inverse controls must be reconstructed independently.""" + payload: Any = field(repr=False) + parameters: Mapping[str, Any] = field(default_factory=dict, repr=False) diff --git a/research/weave/stages/auth.py b/research/weave/stages/auth.py new file mode 100644 index 00000000..dc3cd804 --- /dev/null +++ b/research/weave/stages/auth.py @@ -0,0 +1,10 @@ +"""Optional integrity/replay relation, separate from confidentiality. + +Usage: bind a source-identified Operator to 'auth' in Pipeline. Toggle only 'auth'. +The native transformation is unresolved (Q10); this module declares its +interface and never supplies a fake identity, hash, XOR or conventional-cipher fallback. +No network or storage. See ASSEMBLY.md for the proposed answer and rollback. +""" +from .api import Step + +STEP = Step('auth', ('Q10',), 'Optional integrity/replay relation, separate from confidentiality.', required=False) diff --git a/research/weave/stages/bind.py b/research/weave/stages/bind.py new file mode 100644 index 00000000..ecc711f0 --- /dev/null +++ b/research/weave/stages/bind.py @@ -0,0 +1,10 @@ +"""Native public operation and private-gonol recovery relation. + +Usage: bind a source-identified Operator to 'bind' in Pipeline. Toggle only 'bind'. +The native transformation is unresolved (Q2/Q7/Q8); this module declares its +interface and never supplies a fake identity, hash, XOR or conventional-cipher fallback. +No network or storage. See ASSEMBLY.md for the proposed answer and rollback. +""" +from .api import Step + +STEP = Step('bind', ('Q2', 'Q7', 'Q8'), 'Native public operation and private-gonol recovery relation.', required=True) diff --git a/research/weave/stages/corpus.py b/research/weave/stages/corpus.py new file mode 100644 index 00000000..f9a6ca80 --- /dev/null +++ b/research/weave/stages/corpus.py @@ -0,0 +1,66 @@ +"""Actual-material traversal candidate; native corpus-origin binding remains open. + +Usage: content_route_operator(); parameters['corpus'] = ((material_id, bit_offset), ...). +Selected material bytes are supplied in context.public_material, never taken from a +filename hash. Bit 0 consumes the current left end; bit 1 consumes the right end. +Material bits are MSB-first and repeat cyclically. This concrete rule is a NEW, +explicit assistant proposal for correction, not a recovered requirement or cipher. +Inverse recomputes the traversal from material, without an encoder trace. +""" +from collections import deque +from .api import Blocked, Operator, Step, Streams + + +def route(size: int, material: bytes, offset: int = 0) -> tuple[int, ...]: + if type(size) is not int or size < 0: + raise ValueError('nonnegative bit length required') + if type(material) is not bytes or not material: + raise ValueError('nonempty actual corpus bytes required') + if type(offset) is not int or offset < 0: + raise ValueError('nonnegative corpus bit offset required') + remaining = deque(range(size)) + out = [] + for i in range(size): + p = (offset + i) % (8 * len(material)) + right = (material[p // 8] >> (7 - p % 8)) & 1 + out.append(remaining.pop() if right else remaining.popleft()) + return tuple(out) + + +def transform(value, context, reverse=False): + if not isinstance(value, Streams): + raise TypeError('corpus traversal requires Streams') + choices = context.parameters.get('corpus') + if type(choices) is not tuple or len(choices) != len(value.lanes): + raise Blocked('corpus: select material and bit offset for every lane') + out = [] + for lane, choice in zip(value.lanes, choices): + if type(choice) is not tuple or len(choice) != 2 or type(choice[0]) is not str: + raise ValueError('invalid corpus choice') + label, offset = choice + if label not in context.public_material: + raise Blocked('corpus: forward-accessible material is missing') + order = route(len(lane), context.public_material[label], offset) + if reverse: + restored = [0] * len(lane) + for destination, source in enumerate(order): + restored[source] = lane[destination] + out.append(tuple(restored)) + else: + out.append(tuple(lane[i] for i in order)) + return Streams(tuple(out)) + + +def forward(value, context): + return transform(value, context) + + +def inverse(value, context): + return transform(value, context, True) + + +def content_route_operator() -> Operator: + return Operator(forward, inverse, 'transport/corpus-end-route-v1', scope='transport') + + +STEP = Step('corpus', ('Q4', 'Q7', 'Q8'), 'Native corpus/thread relation remains unresolved.') diff --git a/research/weave/stages/gonol.py b/research/weave/stages/gonol.py new file mode 100644 index 00000000..d49b786b --- /dev/null +++ b/research/weave/stages/gonol.py @@ -0,0 +1,97 @@ +"""Native UCHC word inspection/recovery; whole-message gonol law remains open. + +Usage: with NativeWords(source_path, construct_db, expected_db_sha256) as native: + word = native.promote(b'word'); recovered = native.recover(word) +The inspected upstream source blob is checked BEFORE execution. The database is +opened read-only after matching the caller's snapshot digest. This adapter returns +actual upstream WordGonol objects; it does not construct a replacement gonol tree, +serialize arbitrary binary as fake glyphs, or automatically bind the full stage. +A matching database hash is provenance, not proof of full-corpus construction. +""" +from __future__ import annotations +import hashlib +import importlib.util +from pathlib import Path +import re +import sqlite3 +import sys +from .api import Blocked, Step + +SOURCE_BLOB = '8b55823805c87ad8c4cc9d7451dc2eb90bdedd3a' +SOURCE_REPOSITORY = 'The-Interdependency/uchc' +SOURCE_PATH = 'human/english/english_gonol/hyperspace_construct.py' + + +class NativeWords: + """Read/replay native word records only. Not a general message encoder.""" + def __init__(self, source: Path, database: Path, expected_database_sha256: str): + self.source, self.database = Path(source), Path(database) + self.expected_database_sha256 = expected_database_sha256 + self.db, self.native = None, None + + def __enter__(self): + if (type(self.expected_database_sha256) is not str + or not re.fullmatch('[0-9a-f]{64}', self.expected_database_sha256)): + raise ValueError('explicit lowercase database SHA-256 required') + if not self.source.is_file() or not self.database.is_file(): + raise Blocked('native UCHC source and constructed corpus database are required') + content = self.source.read_bytes() + identity = hashlib.sha1(b'blob ' + str(len(content)).encode() + b'\0' + content).hexdigest() + if identity != SOURCE_BLOB: + raise Blocked('native source differs from inspected UCHC blob; not imported') + with self.database.open('rb') as stream: + actual = hashlib.file_digest(stream, 'sha256').hexdigest() + if actual != self.expected_database_sha256: + raise Blocked('native database snapshot mismatch') + name = '_weave_uchc_' + SOURCE_BLOB + spec = importlib.util.spec_from_file_location(name, self.source) + if spec is None or spec.loader is None: + raise Blocked('cannot load inspected native source') + module = importlib.util.module_from_spec(spec) + sys.modules[name] = module + try: + # Execute precisely the verified bytes, not a later path reread or pyc. + exec(compile(content, str(self.source), 'exec'), module.__dict__) + self.db = sqlite3.connect(self.database.resolve().as_uri() + '?mode=ro', uri=True) + self.db.execute('PRAGMA query_only = ON') + self.db.execute('BEGIN') + self.native = module + except BaseException: + if self.db is not None: + self.db.close() + self.db = None + sys.modules.pop(name, None) + raise + return self + + def promote(self, value: bytes): + if self.db is None or self.native is None: + raise RuntimeError('native reader is not open') + if type(value) is not bytes: + raise TypeError('exact UTF-8 word bytes required') + try: + surface = value.decode('utf-8', errors='strict') + except UnicodeDecodeError as exc: + raise Blocked('arbitrary binary gonol admission remains unresolved') from exc + rows = self.db.execute('SELECT id FROM words WHERE surface = ?', (surface,)).fetchall() + if len(rows) != 1: + raise Blocked('input must match exactly one already-constructed native word') + return self.native.promote_word(self.db, rows[0][0])[0] + + def recover(self, word) -> bytes: + if self.db is None or self.native is None: + raise RuntimeError('native reader is not open') + if type(word) is not self.native.WordGonol: + raise TypeError('the original native WordGonol type is required') + rebuilt = self.native.recover_word(self.db, word.word_id) + if rebuilt != word: + raise ValueError('native identity, constituents, order or surface differs') + return rebuilt.surface.encode('utf-8') + + def __exit__(self, *exc): + if self.db is not None: + self.db.close() + self.db, self.native = None, None + + +STEP = Step('gonol', ('Q1',), 'Complete native plaintext construction and exact recovery.') diff --git a/research/weave/stages/inter.py b/research/weave/stages/inter.py new file mode 100644 index 00000000..e89d9b38 --- /dev/null +++ b/research/weave/stages/inter.py @@ -0,0 +1,64 @@ +"""Literal last/first interleave with explicit or proposed balanced partitions. + +Usage: parameters['inter'] = per-lane tuples of exact partition tuples. +Use balanced_schedule(n, arities) to select the provisional quotient/remainder +rule: first r sections gain one bit, including explicit empty sections if a > n. +Stages consume prior output; section order and all supplied repetitions survive. +There is no three-stage minimum. The sealed run-1 probe is left unchanged. +""" +from probe import inward, inverse_inward +from .api import Blocked, Operator, Step, Streams + + +def balanced_partition(size: int, arity: int) -> tuple[int, ...]: + if type(size) is not int or size < 0 or type(arity) is not int or arity < 3: + raise ValueError('nonnegative size and arity >= 3 required') + q, r = divmod(size, arity) + return tuple(q + (i < r) for i in range(arity)) + + +def balanced_schedule(size: int, arities: tuple[int, ...]) -> tuple[tuple[int, ...], ...]: + if type(arities) is not tuple or not arities: + raise ValueError('supply a nonempty schedule; switch inter off for omission') + return tuple(balanced_partition(size, a) for a in arities) + + +def section(items: tuple, lengths: tuple[int, ...], reverse=False) -> tuple: + if (type(lengths) is not tuple or len(lengths) < 3 + or any(type(v) is not int or v < 0 for v in lengths) + or sum(lengths) != len(items)): + raise ValueError('partition must account for all bits with nonnegative lengths') + transform = inverse_inward if reverse else inward + out, start = [], 0 + for length in lengths: + out.extend(transform(items[start:start + length])) + start += length + return tuple(out) + + +def transform(value, context, reverse=False): + if not isinstance(value, Streams): + raise TypeError('inter requires Streams from an explicit upstream law/profile') + plans = context.parameters.get('inter') + if type(plans) is not tuple or len(plans) != len(value.lanes): + raise Blocked('inter: explicit partitions required for every lane') + out = [] + for lane, stages in zip(value.lanes, plans): + if type(stages) is not tuple or not stages: + raise Blocked('inter: a nonempty stage sequence is required') + for partition in reversed(stages) if reverse else stages: + lane = section(lane, partition, reverse) + out.append(lane) + return Streams(tuple(out)) + + +def forward(value, context): + return transform(value, context) + + +def inverse(value, context): + return transform(value, context, True) + + +STEP = Step('inter', ('Q6',), 'Literal section-local last/first interleave.', + builtin=Operator(forward, inverse, 'literal-section/sequential-empty-aware-v2')) diff --git a/research/weave/stages/join.py b/research/weave/stages/join.py new file mode 100644 index 00000000..4f95f041 --- /dev/null +++ b/research/weave/stages/join.py @@ -0,0 +1,61 @@ +"""Explicit stable lane interlacing; not an invented secret native join law. + +Usage: parameters['join'] = (lane_lengths, lane_order). Visit each named lane once +per cycle and skip only exhausted lanes. Exact lengths/order come independently +from the research profile on both sides, not from an encoder trace or ciphertext. +""" +from .api import Blocked, Operator, Step, Streams + + +def route(lengths: tuple[int, ...], order: tuple[int, ...]) -> tuple[int, ...]: + if (type(lengths) is not tuple or not lengths + or any(type(n) is not int or n < 0 for n in lengths)): + raise ValueError('invalid lane lengths') + if (type(order) is not tuple or any(type(i) is not int for i in order) + or sorted(order) != list(range(len(lengths)))): + raise ValueError('lane order must visit every lane exactly once per cycle') + left, out = list(lengths), [] + while any(left): + for owner in order: + if left[owner]: + out.append(owner) + left[owner] -= 1 + return tuple(out) + + +def plan(context): + value = context.parameters.get('join') + if type(value) is not tuple or len(value) != 2: + raise Blocked('join: explicit lengths and lane order required') + lengths, order = value + return lengths, route(lengths, order) + + +def forward(value, context): + lengths, owners = plan(context) + if not isinstance(value, Streams) or tuple(map(len, value.lanes)) != lengths: + raise ValueError('join input lengths differ from declared route') + cursors, out = [0] * len(lengths), [] + for owner in owners: + out.append(value.lanes[owner][cursors[owner]]) + cursors[owner] += 1 + return Streams((tuple(out),)) + + +def inverse(value, context): + lengths, owners = plan(context) + if not isinstance(value, Streams) or len(value.lanes) != 1: + raise TypeError('join inverse requires one interlaced stream') + if len(value.lanes[0]) != len(owners): + raise ValueError('joined length differs from declared route') + lanes = [[] for _ in lengths] + for bit, owner in zip(value.lanes[0], owners): + lanes[owner].append(bit) + return Streams(tuple(tuple(lane) for lane in lanes)) + + +def explicit_join_operator() -> Operator: + return Operator(forward, inverse, 'transport/explicit-stable-join-v1', scope='transport') + + +STEP = Step('join', ('Q5', 'Q8'), 'Native key/context-derived join law remains unresolved.') diff --git a/research/weave/stages/key.py b/research/weave/stages/key.py new file mode 100644 index 00000000..7095c9c7 --- /dev/null +++ b/research/weave/stages/key.py @@ -0,0 +1,33 @@ +"""Public/private setup boundary; no substitute key generator. + +Usage: call keygen(explicit_native_law, private_gonol, material, randomness). +The explicit law owns the mathematical public/private relation (Q2, Q7, Q8). +No network, persistence, private-key logging or conventional crypto fallback. +""" +from dataclasses import dataclass, field +from typing import Any, Callable +from .api import Blocked + + +@dataclass(frozen=True) +class KeyPair: + public: Any = field(repr=False) + private: Any = field(repr=False) + law_identity: str + + +def keygen(law: Callable[..., KeyPair] | None, *, private_gonol: Any, + material: Any, randomness: bytes, enabled: bool = True) -> KeyPair | None: + if type(enabled) is not bool: + raise TypeError("key switch must be Boolean") + if not enabled: + return None + if law is None: + raise Blocked('key: Q2/Q7/Q8 require an actual native KeyGen relation') + result = law(private_gonol=private_gonol, material=material, randomness=randomness) + if (not isinstance(result, KeyPair) or type(result.law_identity) is not str + or not result.law_identity.strip()): + raise TypeError('KeyGen must return a source-identified KeyPair') + if result.public is None or result.private is None: + raise ValueError('KeyGen returned an absent key side') + return result diff --git a/research/weave/stages/split.py b/research/weave/stages/split.py new file mode 100644 index 00000000..f0d67a81 --- /dev/null +++ b/research/weave/stages/split.py @@ -0,0 +1,61 @@ +"""Explicit occurrence routing, not the missing native thread projection law. + +Usage: Operator = explicit_route_operator(); context.parameters['split'] is +(thread_count, owners), with one owner per incoming bit occurrence. The cyclic +route helper is a named transport-only proposal. No native law is auto-bound. +No data copies per lane: every occurrence is assigned once, including empty lanes. +""" +from .api import Blocked, Operator, Step, Streams + + +def cyclic_route(size: int, count: int) -> tuple[int, ...]: + """Provisional transport routing; gives no per-thread confidentiality.""" + if type(size) is not int or size < 0 or type(count) is not int or count < 3: + raise ValueError('nonnegative length and thread count >= 3 required') + return tuple(i % count for i in range(size)) + + +def plan(context): + value = context.parameters.get('split') + if type(value) is not tuple or len(value) != 2: + raise Blocked('split: provide (thread_count, occurrence_owners)') + count, owners = value + if type(count) is not int or count < 3 or type(owners) is not tuple: + raise ValueError('invalid thread plan') + if any(type(i) is not int or not 0 <= i < count for i in owners): + raise ValueError('route contains an invalid owner') + return count, owners + + +def forward(value, context): + if not isinstance(value, Streams) or len(value.lanes) != 1: + raise TypeError('split requires one serialized input stream; not a gonol substitute') + count, owners = plan(context) + if len(owners) != len(value.lanes[0]): + raise ValueError('split route does not cover every input occurrence') + lanes = [[] for _ in range(count)] + for bit, owner in zip(value.lanes[0], owners): + lanes[owner].append(bit) + return Streams(tuple(tuple(lane) for lane in lanes)) + + +def inverse(value, context): + count, owners = plan(context) + if not isinstance(value, Streams) or len(value.lanes) != count: + raise ValueError('split inverse lane count mismatch') + sizes = tuple(owners.count(i) for i in range(count)) + if tuple(map(len, value.lanes)) != sizes: + raise ValueError('split inverse occurrence counts differ') + cursors = [0] * count + out = [] + for owner in owners: + out.append(value.lanes[owner][cursors[owner]]) + cursors[owner] += 1 + return Streams((tuple(out),)) + + +def explicit_route_operator() -> Operator: + return Operator(forward, inverse, 'transport/explicit-occurrence-route-v1', scope='transport') + + +STEP = Step('split', ('Q3',), 'Native complementary thread law remains unresolved.') diff --git a/research/weave/stages/whole.py b/research/weave/stages/whole.py new file mode 100644 index 00000000..30b37381 --- /dev/null +++ b/research/weave/stages/whole.py @@ -0,0 +1,26 @@ +"""Specified last/first operation over one complete joined stream. + +Usage: bind no additional law; supply Streams((joined_bits,)). Multiple lanes are an +incompatible ablation until a real join law exists; this stage never joins implicitly. +No network/storage. Disable only 'whole' to measure its contribution. +""" +from probe import inward, inverse_inward +from .api import Operator, Step, Streams + + +def transform(value, reverse=False): + if not isinstance(value, Streams) or len(value.lanes) != 1: + raise TypeError('whole requires one already joined stream; no implicit join') + return Streams(((inverse_inward if reverse else inward)(value.lanes[0]),)) + + +def forward(value, context): + return transform(value) + + +def inverse(value, context): + return transform(value, True) + + +STEP = Step('whole', ('Q5',), 'Last/first interleave over the complete joined stream.', + builtin=Operator(forward, inverse, 'literal-whole-inward-v1')) diff --git a/research/weave/test.py b/research/weave/test.py new file mode 100644 index 00000000..6d247d0e --- /dev/null +++ b/research/weave/test.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +"""Run all local assembly/transport tests and emit an honest source-bound receipt. + +Usage: python test.py --receipt /tmp/weave-check.json +No dependency installation or network. Native corpus replay is NOT executed by +this suite, and missing native encryption laws are reported separately from PASS. +""" +import argparse +import hashlib +import io +import json +from pathlib import Path +import platform +import sys +import unittest +from assembly import Pipeline + +ROOT = Path(__file__).resolve().parent + + +def snapshot(): + paths = list(ROOT.rglob('*.py')) + list((ROOT/'profiles').glob('*.json')) + return {str(p.relative_to(ROOT)): hashlib.sha256(p.read_bytes()).hexdigest() + for p in sorted(paths) if '__pycache__' not in p.parts} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--receipt', type=Path) + args = parser.parse_args() + before = snapshot() + report = io.StringIO() + result = unittest.TextTestRunner(stream=report, verbosity=2).run( + unittest.defaultTestLoader.discover(str(ROOT/'tests'))) + after = snapshot() + passed = (result.wasSuccessful() and not result.skipped + and not result.expectedFailures and not result.unexpectedSuccesses + and result.testsRun == 43 and before == after) + receipt = { + 'schema':'weave.transport-evidence/v1', + 'status':'PASSED' if passed else 'FAILED', + 'python':platform.python_version(), + 'tests':result.testsRun, + 'failures':len(result.failures),'errors':len(result.errors),'skipped':len(result.skipped), + 'source_unchanged':before == after, + 'expected_test_methods':43, + 'coverage_status':'WITNESSED' if passed else 'NOT_ACCEPTED', + 'source_sha256':hashlib.sha256(json.dumps(before,sort_keys=True,separators=(',',':')).encode()).hexdigest(), + 'source_files':before, + 'coverage':{'binary_messages':8191,'binary_lengths_inclusive':[0,12], + 'assembly_switch_masks':512,'transport_switch_masks':32, + 'valid_transport_masks':28,'incompatible_transport_masks':4, + 'largest_roundtrip_bytes':65536,'separate_process_roundtrip_bytes':1026}, + 'full_weave':Pipeline().plan(), + 'native_corpus_replay':'NOT_EXECUTED: source inspected; no constructed database materialized here', + 'native_adapter_tests':'missing-input/source-identity refusal only; not successful real-corpus replay', + 'security_observations':['fixed-map recovery succeeds against this transport candidate at 137 bits in 8 queries', + 'wrong material can yield wrong plaintext without an authentication error'], + 'nonclaim':'No complete native Weave or asymmetric security result; no independent researcher review.' + } + sys.stderr.write(report.getvalue()) + if args.receipt: + args.receipt.parent.mkdir(parents=True,exist_ok=True) + args.receipt.write_text(json.dumps(receipt,indent=2)+'\n') + print(json.dumps({key:receipt[key] for key in ('status','tests','failures','errors','skipped','source_sha256')})) + return 0 if passed else 1 + + +if __name__=='__main__':raise SystemExit(main()) diff --git a/research/weave/tests/test_assembly.py b/research/weave/tests/test_assembly.py new file mode 100644 index 00000000..cadbb13e --- /dev/null +++ b/research/weave/tests/test_assembly.py @@ -0,0 +1,176 @@ +"""Assembly witnesses, NOT a cipher/security test suite. + +Usage: python -m unittest discover -s tests -p 'test_assembly.py'. +Finite preflight: 512 switch configurations over six synthetic bits; no corpus, +secrets, network or provider calls. Marker operators are explicitly WIRING_ONLY. +""" +from dataclasses import replace +from itertools import product +import unittest +from assembly import DEFAULTS, Pipeline, Switches +from stages import STEPS +from stages.api import Blocked, Operator, PrivateContext, PublicContext, Streams, Transition +from stages.key import keygen + +DATA = Streams(((0, 1, 0, 1, 1, 0),)) +PARAMS = {'inter': (((2, 2, 2),),)} +PUB = PublicContext('test-public-only', PARAMS) +PRIV = PrivateContext('test-public-only', 'test-private-only', PARAMS) + + +def markers(log): + def pair(name): + def f(value, context): + log.append(('forward', name)) + return value + def r(value, context): + log.append(('inverse', name)) + return value + return Operator(f, r, 'wiring-fixture/' + name, fixture=True) + return {s.name: pair(s.name) for s in STEPS if s.builtin is None} + + +class AssemblyTests(unittest.TestCase): + def test_all_on_is_blocked_before_input(self): + touched = [] + handlers = markers(touched) + del handlers['bind'] + with self.assertRaisesRegex(Blocked, 'bind: Q2/Q7/Q8'): + Pipeline(operators=handlers, allow_fixtures=True).encrypt(DATA, PUB) + self.assertEqual(touched, []) + + def test_default_scope_and_six_unresolved_bindings(self): + plan = Pipeline().plan() + self.assertFalse(plan['complete_cipher_implemented']) + self.assertEqual(plan['status'], 'BLOCKED') + self.assertEqual(len(plan['missing']), 6) + self.assertFalse(plan['switches']['auth']) + self.assertTrue(all(plan['switches'][s.name] for s in STEPS if s.required)) + + def test_no_native_fixture_promotion(self): + with self.assertRaisesRegex(Blocked, 'wiring fixture'): + Pipeline(operators=markers([])).encrypt(DATA, PUB) + + def test_all_512_switches_keep_identity_or_report_incompatibility(self): + for values in product((False, True), repeat=len(DEFAULTS)): + flags = dict(zip(DEFAULTS, values)) + p = Pipeline(Switches(flags), markers([]), allow_fixtures=True) + if flags['bind'] and not (flags['key'] and flags['gonol']): + with self.assertRaisesRegex(Blocked, 'incompatible ablation'): + p.encrypt(DATA, PUB) + continue + forward = p.encrypt(DATA, PUB) + inverse = p.decrypt(forward, PRIV) + self.assertEqual(inverse.payload, DATA) + self.assertEqual(dict((n, v) for n,v in p.switches.values.items()), flags) + self.assertNotEqual(forward.classification, 'FULL_PROFILE_EXPERIMENT') + for name, status in forward.events: + self.assertEqual(status == 'OFF', not flags[name]) + + def test_inverse_order(self): + log = [] + p = Pipeline(operators=markers(log), allow_fixtures=True) + e = p.encrypt(DATA, PUB) + p.decrypt(e, PRIV) + forward = [name for direction,name in log if direction == 'forward'] + inverse = [name for direction,name in log if direction == 'inverse'] + self.assertEqual(inverse, list(reversed(forward))) + self.assertEqual(e.classification, 'WIRING_ONLY') + + def test_cipher_never_receives_private_context(self): + with self.assertRaisesRegex(TypeError, 'PublicContext only'): + Pipeline().encrypt(DATA, PRIV) + self.assertFalse(hasattr(PUB, 'private_key')) + + def test_context_and_run_repr_do_not_expose_material(self): + self.assertNotIn('test-private-only', repr(PRIV)) + p = Pipeline(Switches({n:False for n in DEFAULTS})) + e = p.encrypt(b'test-plaintext-not-a-cipher', PUB) + self.assertNotIn('test-plaintext', repr(e)) + self.assertEqual(e.classification, 'ABLATION_ONLY') + + def test_independent_switch_no_cascade(self): + config = Switches({'corpus': False}) + self.assertTrue(config['split']) + self.assertTrue(config['inter']) + self.assertTrue(config['join']) + + def test_unknown_and_nonboolean_switches_fail(self): + for flags in ({'corups': False}, {'inter': 0}, {'corpus': 'off'}): + with self.assertRaises((ValueError, TypeError)): + Switches(flags) + + def test_no_silent_literal_override(self): + fake = Operator(lambda x,c:x, lambda x,c:x, 'fake') + with self.assertRaisesRegex(ValueError, 'cannot be replaced'): + Pipeline(operators={'inter':fake}) + + def test_exact_one_stage_is_accepted_and_reversed(self): + flags = {n:False for n in DEFAULTS} + flags['inter'] = flags['whole'] = True + p = Pipeline(Switches(flags)) + e = p.encrypt(DATA, PUB) + self.assertEqual(p.decrypt(e, PRIV).payload, DATA) + self.assertEqual(e.classification, 'ABLATION_ONLY') + + def test_missing_partition_never_guessed(self): + flags = {n:False for n in DEFAULTS}; flags['inter'] = True + with self.assertRaisesRegex(Blocked, 'explicit partitions'): + Pipeline(Switches(flags)).encrypt(DATA, PublicContext(None)) + + def test_whole_never_implicitly_joins_threads(self): + flags = {n:False for n in DEFAULTS}; flags['whole'] = True + with self.assertRaisesRegex(TypeError, 'already joined'): + Pipeline(Switches(flags)).encrypt(Streams(((0,1),(1,0))), PUB) + + def test_changed_inverse_switch_mask_rejected(self): + flags = {n:False for n in DEFAULTS} + e = Pipeline(Switches(flags)).encrypt(DATA, PUB) + flags['whole'] = True + with self.assertRaisesRegex(ValueError, 'recipe'): + Pipeline(Switches(flags)).decrypt(e, PRIV) + + def test_missing_key_relation_is_not_invented(self): + with self.assertRaisesRegex(Blocked, 'KeyGen relation'): + keygen(None, private_gonol=object(), material={}, randomness=b'') + + def test_stage_can_pass_derived_plan_without_exporting_it(self): + log = [] + ops = markers(log) + secret_plan = {'inter':PARAMS['inter'], 'do_not_publish':'secret-marker'} + def forward(value, context): + return Transition(value, secret_plan) + ops['corpus'] = Operator(forward, lambda x,c:x, 'wiring-fixture/derived', fixture=True) + e = Pipeline(operators=ops, allow_fixtures=True).encrypt(DATA, PublicContext('test-public')) + self.assertNotIn('secret-marker', repr(e)) + self.assertFalse(hasattr(e, 'parameters')) + + def test_key_off_does_not_invoke_keygen(self): + def forbidden(**kwargs): + raise AssertionError('disabled generator was called') + self.assertIsNone(keygen(forbidden, private_gonol=object(), material={}, + randomness=b'', enabled=False)) + + def test_key_off_removes_keys_from_operator_contexts(self): + seen = [] + def f(value, context): + seen.append(context.public_key) + return value + def r(value, context): + seen.extend((context.public_key, context.private_key)) + return value + flags = {n:False for n in DEFAULTS}; flags['corpus'] = True + p = Pipeline(Switches(flags), {'corpus':Operator(f,r,'wiring-fixture/keys-off',True)}, + allow_fixtures=True) + e = p.encrypt(DATA,PUB) + p.decrypt(e,PRIV) + self.assertEqual(seen,[None,None,None]) + + def test_streams_reject_bool_and_mutable_input(self): + for lanes in (((True,),), ([0,1],), []): + with self.assertRaises(TypeError): + Streams(lanes) + + +if __name__ == '__main__': + unittest.main() diff --git a/research/weave/tests/test_transport.py b/research/weave/tests/test_transport.py new file mode 100644 index 00000000..93dab34f --- /dev/null +++ b/research/weave/tests/test_transport.py @@ -0,0 +1,259 @@ +"""Executable provisional composition tests, not native Weave security acceptance. + +Usage: python -m unittest discover -s tests. Fixed scope: 8,191 binary messages; +32 transport switch masks; separate-process byte recovery; bounded resource and +malformed-input checks. Temporary nonsecret corpus fixtures only. No network. +""" +from copy import deepcopy +from dataclasses import replace +from itertools import product +import json +from pathlib import Path +import subprocess +import sys +import tempfile +import unittest +from assembly import DEFAULTS, Pipeline, Switches +from stages.api import Blocked, Operator, PrivateContext, PublicContext, Streams +from stages.gonol import NativeWords +from stages import corpus, inter, join, split +from transport import build, byte_stream, recover_bytes, LIMIT +from lab import wire, unwire, strict_json, parse_lanes +from probe import inward, section_stage, recover_fixed_map, recover_with_map + +ROOT = Path(__file__).resolve().parents[1] +PROFILE = json.loads((ROOT/'profiles/transport.json').read_text()) +MATERIAL = {'a': b'Actual literary-like fixture.', 'b': bytes(range(64)), 'c': b'\x00\xff\x55\xaa'} + + +def flags(**changes): + p = deepcopy(PROFILE) + p['switches'].update(changes) + return p + + +def two_way(data, profile=PROFILE, material=MATERIAL): + p, public, _ = build(deepcopy(profile), len(data), dict(material)) + encoded = p.encrypt(Streams((data,)), public) + # Build an independent context; pass no earlier Transition or stage snapshot. + q, _, private = build(deepcopy(profile), len(data), dict(material)) + decoded = q.decrypt(unwire(wire(encoded), q), private) + return encoded, decoded + + +class TransportTests(unittest.TestCase): + def test_complete_profile_still_refuses(self): + with self.assertRaises(Blocked): + Pipeline().encrypt(b'not touched', PublicContext(None)) + self.assertEqual(len(Pipeline().plan()['missing']), 6) + + def test_no_native_promotion_by_switching_flags(self): + p = flags(key=True, gonol=True, bind=True) + with self.assertRaises(Blocked): + build(p, 32, MATERIAL) + + def test_full_transport_all_8191_binary_messages(self): + count = 0 + for n in range(13): + for value in range(1 << n): + data = tuple((value >> i) & 1 for i in range(n)) + encoded, decoded = two_way(data) + self.assertEqual(decoded.payload, Streams((data,))) + self.assertEqual(encoded.classification, 'TRANSPORT_CANDIDATE') + self.assertEqual(sum(map(len, encoded.payload.lanes)), n) + count += 1 + self.assertEqual(count, 8191) + + def test_all_32_transport_masks(self): + valid = invalid = 0 + names = ('split','corpus','inter','join','whole') + data = tuple((i*i + i//3) % 2 for i in range(137)) + for bits in product((False, True), repeat=5): + p = flags(**dict(zip(names, bits))) + if not p['switches']['split']: + # Explicitly adapt this test recipe to one lane; no runner cascade. + p['corpus'], p['arities'], p['join_order'] = [['a',0]], [[5,7,3]], [0] + if p['switches']['split'] and not p['switches']['join'] and p['switches']['whole']: + with self.assertRaisesRegex(ValueError, 'incompatible ablation'): + build(p, len(data), MATERIAL) + invalid += 1 + continue + encoded, decoded = two_way(data, p) + self.assertEqual(decoded.payload, Streams((data,))) + for name, status in encoded.events: + self.assertEqual(status == 'EXECUTED', p['switches'][name]) + valid += 1 + self.assertEqual((valid, invalid), (28,4)) + + def test_balanced_sizes_and_empty_sections(self): + for n in range(513): + for a in (3,5,7,11,31): + plan = inter.balanced_partition(n,a) + self.assertEqual(sum(plan), n) + self.assertEqual(len(plan), a) + self.assertLessEqual(max(plan)-min(plan),1) + ids = tuple(range(n)) + self.assertEqual(inter.section(inter.section(ids,plan),plan,True),ids) + self.assertEqual(inter.balanced_partition(2,5),(1,1,0,0,0)) + self.assertEqual(inter.balanced_partition(0,3),(0,0,0)) + + def test_old_equal_section_behavior_preserved(self): + for n in (105,210,840): + for a in (3,5,7): + ids = tuple(range(n)); lengths = (n//a,)*a + self.assertEqual(inter.section(ids,lengths),section_stage(ids,lengths)) + + def test_one_stage_not_three_stages(self): + p = deepcopy(PROFILE); p['arities'] = [[3],[5],[7]] + data = tuple(i%2 for i in range(61)) + self.assertEqual(two_way(data,p)[1].payload,Streams((data,))) + + def test_corpus_content_really_changes_route(self): + self.assertEqual(corpus.route(7,b'\x00'),tuple(range(7))) + self.assertEqual(corpus.route(7,b'\xff'),tuple(reversed(range(7)))) + self.assertEqual(corpus.route(7,b'\xaa'),(6,0,5,1,4,2,3)) + data = tuple([1,0,1,1,0,0,0]*17) + a = two_way(data,material={'a':b'\x00','b':b'\x00','c':b'\x00'})[0] + b = two_way(data,material={'a':b'\xff','b':b'\xff','c':b'\xff'})[0] + self.assertNotEqual(a.payload,b.payload) + + def test_material_names_do_not_act_as_keys(self): + p = deepcopy(PROFILE) + p['corpus'] = [['x',0],['y',1],['z',2]] + renamed = dict(zip(('x','y','z'),(MATERIAL['a'],MATERIAL['b'],MATERIAL['c']))) + data = tuple(i%2 for i in range(137)) + self.assertEqual(two_way(data)[0].payload,two_way(data,p,renamed)[0].payload) + + def test_corpus_off_has_no_material_influence(self): + p = flags(corpus=False) + data = tuple(i%2 for i in range(137)) + self.assertEqual(two_way(data,p,{})[0].payload,two_way(data,p,MATERIAL)[0].payload) + _, public, private = build(p,len(data),MATERIAL) + self.assertEqual(dict(public.public_material),{}) + self.assertEqual(dict(private.public_material),{}) + self.assertNotIn('corpus',public.parameters) + + def test_disabled_stage_not_called(self): + def forbidden(*args): + raise AssertionError('disabled stage executed') + off = {n:False for n in DEFAULTS} + p = Pipeline(Switches(off), {'corpus':Operator(forbidden,forbidden,'test/forbidden')}) + value = b'exact unchanged opaque object' + e = p.encrypt(value,PublicContext(None)) + self.assertIs(e.payload,value) + + def test_join_order_and_empty_lanes(self): + self.assertEqual(join.route((2,0,1),(2,0,1)),(2,0,0)) + self.assertEqual(join.route((0,0,0),(0,1,2)),()) + for sizes in product(range(4),repeat=3): + r = join.route(sizes,(2,0,1)) + self.assertEqual(tuple(r.count(i) for i in range(3)),sizes) + + def test_bad_routes_and_partitions_refused(self): + for bad in ((0,0,1),(True,1,2),(3,1,0)): + with self.assertRaises(ValueError):join.route((1,2,3),bad) + for bad in ((1,1,-1),(True,1,1),(1,1),(0,0,0)): + with self.assertRaises(ValueError):inter.section((0,1,0),bad) + for n,a in ((True,3),(1,True),(-1,3),(1,2)): + with self.assertRaises(ValueError):inter.balanced_partition(n,a) + with self.assertRaises(ValueError):corpus.route(3,b'') + with self.assertRaises(ValueError):corpus.route(3,b'a',True) + + def test_unknown_and_nonboolean_profile_fields_refused(self): + for p in (flags(corups=False),flags(inter=0),flags(corpus='false')): + with self.assertRaises((ValueError,TypeError)):build(p,16,MATERIAL) + p=deepcopy(PROFILE);p['typo']=1 + with self.assertRaises(ValueError):build(p,16,MATERIAL) + + def test_missing_material_never_an_identity(self): + with self.assertRaises(Blocked):build(PROFILE,32,{}) + + def test_wrong_material_is_not_authenticated(self): + data = tuple([1,0,1,1,0,0,0]*17) + p,pub,_ = build(PROFILE,len(data),{'a':b'\x00','b':b'\x00','c':b'\x00'}) + e = p.encrypt(Streams((data,)),pub) + q,_,priv = build(PROFILE,len(data),{'a':b'\xff','b':b'\xff','c':b'\xff'}) + decoded = q.decrypt(e,priv) + self.assertNotEqual(decoded.payload,Streams((data,))) + self.assertNotEqual(decoded.classification,'FULL_PROFILE_EXPERIMENT') + + def test_fixed_map_attack_only_on_this_complete_transport_profile(self): + n=137 + p,pub,_=build(PROFILE,n,MATERIAL) + def oracle(bits):return p.encrypt(Streams((bits,)),pub).payload.lanes[0] + mapping,queries=recover_fixed_map(oracle,n) + data=tuple((i*i+i//3)%2 for i in range(n)) + self.assertEqual(recover_with_map(oracle(data),mapping),data) + self.assertEqual(queries,8) + + def test_packet_rejects_malformed_data_and_recipe(self): + p,pub,_=build(PROFILE,8,MATERIAL) + e=p.encrypt(byte_stream(b'A'),pub) + obj=json.loads(wire(e));obj['recipe'][0][1]='invented' + with self.assertRaises(ValueError):unwire(json.dumps(obj).encode(),p) + for records in ([{'bits':1,'hex':'ff'}],[{'bits':True,'hex':'00'}],[], + [{'bits':8,'hex':'FF'}],[{'bits':8,'hex':'00','other':1}]): + with self.assertRaises(ValueError):parse_lanes(records) + with self.assertRaises(ValueError):strict_json('{"x":1,"x":2}') + + def test_byte_admission_is_explicit_not_a_gonol(self): + data=bytes(range(256))+b'\x00\xff' + self.assertEqual(recover_bytes(byte_stream(data)),data) + self.assertIsInstance(byte_stream(data),Streams) + with self.assertRaises(ValueError):recover_bytes(Streams(((1,),))) + + def test_native_missing_and_wrong_source_refuse_before_execution(self): + with self.assertRaises(Blocked): + with NativeWords(Path('/nonexistent/source'),Path('/nonexistent/db'),'0'*64):pass + with tempfile.TemporaryDirectory() as directory: + root=Path(directory);source=root/'native.py';database=root/'construct.db' + marker=root/'executed' + source.write_text(f"open({str(marker)!r}, 'w').write('bad')") + database.write_bytes(b'not a real constructed database') + with self.assertRaisesRegex(Blocked,'not imported'): + with NativeWords(source,database,'0'*64):pass + self.assertFalse(marker.exists()) + + def test_resource_boundary_is_explicit(self): + with self.assertRaisesRegex(ValueError,'resource limit'):build(PROFILE,LIMIT*8+1,MATERIAL) + p=deepcopy(PROFILE);p['arities'][0]=[4097] + with self.assertRaisesRegex(ValueError,'resource profile'):build(p,8,MATERIAL) + + def test_full_resource_limit_roundtrip(self): + data = bytes(range(256)) * 256 + p, public, _ = build(PROFILE, len(data)*8, MATERIAL) + e = p.encrypt(byte_stream(data), public) + q, _, private = build(deepcopy(PROFILE), len(data)*8, dict(MATERIAL)) + restored = q.decrypt(unwire(wire(e), q), private) + self.assertEqual(recover_bytes(restored.payload), data) + + def test_fresh_process_roundtrip_and_no_overwrite(self): + with tempfile.TemporaryDirectory() as directory: + root=Path(directory);config=root/'p.json';src=root/'i.bin';dst=root/'o.lab';out=root/'r.bin' + config.write_text(json.dumps(PROFILE)); data=bytes(range(256))*4+b'\x00\xff' + src.write_bytes(data);args=[] + for label,content in MATERIAL.items(): + path=root/(label+'.bin');path.write_bytes(content) + args+=['--material',f'{label}={path}'] + enc=[sys.executable,str(ROOT/'run.py'),'enc',str(config),str(src),str(dst),*args] + dec=[sys.executable,str(ROOT/'run.py'),'dec',str(config),str(dst),str(out),*args] + first=subprocess.run(enc,capture_output=True,text=True) + self.assertEqual(first.returncode,0,first.stderr) + src.unlink() # The original input is not available to the recovery process. + second=subprocess.run(dec,capture_output=True,text=True) + self.assertEqual(second.returncode,0,second.stderr) + self.assertEqual(out.read_bytes(),data) + again=subprocess.run(dec,capture_output=True,text=True) + self.assertEqual(again.returncode,2) + self.assertEqual(out.read_bytes(),data) + + def test_off_corpus_does_not_read_supplied_path(self): + with tempfile.TemporaryDirectory() as directory: + root=Path(directory);config=root/'p.json';src=root/'i';dst=root/'o' + config.write_text(json.dumps(flags(corpus=False)));src.write_bytes(b'test') + result=subprocess.run([sys.executable,str(ROOT/'run.py'),'enc',str(config),str(src), + str(dst),'--material','a=/nonexistent/never-read'],capture_output=True,text=True) + self.assertEqual(result.returncode,0,result.stderr) + + +if __name__=='__main__':unittest.main() diff --git a/research/weave/transport.py b/research/weave/transport.py new file mode 100644 index 00000000..a15ba4e6 --- /dev/null +++ b/research/weave/transport.py @@ -0,0 +1,93 @@ +"""Explicit provisional composition; native whole-Weave profile stays separate. + +Usage: build(profile, bit_length, materials) independently on each side. Returned +contexts contain freshly compiled controls, not captured encoder state. Profile +files are research inputs, NOT a public key or a proposed ciphertext header. +The CLI limits one message to 64 KiB as an implementation resource guard. +""" +from assembly import Pipeline, Switches +from stages.api import Blocked, PrivateContext, PublicContext, Streams +from stages.split import cyclic_route, explicit_route_operator +from stages.corpus import content_route_operator +from stages.inter import balanced_schedule +from stages.join import explicit_join_operator + +SCHEMA = 'weave.transport-profile/v1' +LIMIT = 65536 +REQUIRED = {'schema', 'switches', 'threads', 'arities', 'join_order', 'corpus'} + + +def build(profile: dict, size: int, materials: dict): + if type(profile) is not dict or set(profile) != REQUIRED or profile['schema'] != SCHEMA: + raise ValueError('unknown, missing or unsupported transport profile fields') + if type(size) is not int or not 0 <= size <= LIMIT * 8: + raise ValueError('transport resource limit: at most 64 KiB per message') + if type(profile['switches']) is not dict: + raise TypeError('switches must be an object') + switches = Switches(profile['switches']) + if any(switches[s] for s in ('key', 'gonol', 'bind', 'auth')): + raise Blocked('transport profile cannot supply key/gonol/bind/auth native laws') + count = profile['threads'] + if type(count) is not int or not 3 <= count <= 63: + raise ValueError('transport resource profile admits 3..63 threads') + params = {} + if switches['split']: + owners = cyclic_route(size, count) + params['split'] = (count, owners) + lengths = tuple(owners.count(i) for i in range(count)) + else: + lengths = (size,) + if switches['corpus']: + choices = profile['corpus'] + if type(choices) is not list or len(choices) != len(lengths): + raise ValueError('choose actual material/offset for each active lane') + clean = [] + for choice in choices: + if (type(choice) is not list or len(choice) != 2 or type(choice[0]) is not str + or type(choice[1]) is not int or choice[1] < 0): + raise ValueError('invalid corpus choice') + if choice[0] not in materials or type(materials[choice[0]]) is not bytes or not materials[choice[0]]: + raise Blocked('missing nonempty corpus material') + clean.append(tuple(choice)) + params['corpus'] = tuple(clean) + used_material = {label: materials[label] for label, offset in clean} + else: + used_material = {} + if switches['inter']: + arities = profile['arities'] + if type(arities) is not list or len(arities) != len(lengths): + raise ValueError('one arity schedule per active lane required') + for schedule in arities: + if (type(schedule) is not list or not 1 <= len(schedule) <= 64 + or any(type(a) is not int or not 3 <= a <= 4096 for a in schedule)): + raise ValueError('resource profile: 1..64 stages with arities 3..4096') + params['inter'] = tuple(balanced_schedule(n, tuple(a)) for n, a in zip(lengths, arities)) + if switches['join']: + order = profile['join_order'] + if (type(order) is not list or any(type(i) is not int for i in order) + or sorted(order) != list(range(len(lengths)))): + raise ValueError('join_order must visit all active lanes') + params['join'] = (lengths, tuple(order)) + elif switches['whole'] and len(lengths) != 1: + raise ValueError('incompatible ablation: whole needs one lane; join is OFF') + pipeline = Pipeline(switches, { + 'split': explicit_route_operator(), + 'corpus': content_route_operator(), + 'join': explicit_join_operator(), + }) + public = PublicContext(None, params, public_material=used_material) + private = PrivateContext(None, None, params, public_material=used_material) + return pipeline, public, private + + +def byte_stream(data: bytes) -> Streams: + if type(data) is not bytes or len(data) > LIMIT: + raise ValueError('transport input must be bytes of at most 64 KiB') + return Streams((tuple((byte >> shift) & 1 for byte in data for shift in range(7, -1, -1)),)) + + +def recover_bytes(streams: Streams) -> bytes: + if not isinstance(streams, Streams) or len(streams.lanes) != 1 or len(streams.lanes[0]) % 8: + raise ValueError('recovered input is not one byte-aligned stream') + bits = streams.lanes[0] + return bytes(sum(bits[i + j] << (7-j) for j in range(8)) for i in range(0, len(bits), 8)) diff --git a/research/weave/verify.py b/research/weave/verify.py new file mode 100644 index 00000000..30ba7e87 --- /dev/null +++ b/research/weave/verify.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +"""Independent implementation of the positional checks; does not import probe.py. +Usage: python verify.py receipt.json > verified.json +Reads local source/receipt bytes; no network, no secrets, no system write except stdout. +Owner: Erin Spencer's Stack research. Rollback: remove this evidence verifier. +This is a second implementation by the same assistant, not an independent author review. +""" +from __future__ import annotations +import hashlib +import itertools +import json +from pathlib import Path +import sys + + +def must(test: bool, message: str) -> None: + if not test: + raise ValueError(message) + + +def sha(value: object) -> str: + return hashlib.sha256(json.dumps(value, sort_keys=True, + separators=(",", ":")).encode()).hexdigest() + + +def end_map(n: int) -> tuple[int, ...]: + # Analytic forward index at output j; no deque and no encoder import. + return tuple(n-1-j//2 if j % 2 == 0 else j//2 for j in range(n)) + + +def transform_map(n: int, arities: tuple[int, ...]) -> tuple[int, ...]: + p = tuple(range(n)) + for arity in arities: + must(n % arity == 0, "not equal partition") + width = n // arity + local = end_map(width) + q = tuple((j//width)*width + local[j % width] for j in range(n)) + p = tuple(p[index] for index in q) + return tuple(p[index] for index in end_map(n)) + + +def main() -> int: + path = Path(sys.argv[1] if len(sys.argv) > 1 else "receipt.json").resolve() + r = json.loads(path.read_text()) + root = path.parent + for name, expected in r["source_inputs"]["local_sha256"].items(): + must(hashlib.sha256((root/name).read_bytes()).hexdigest() == expected, + f"source mismatch: {name}") + must(end_map(5) == (4, 0, 3, 1, 2), "literal odd golden vector") + must(end_map(6) == (5, 0, 4, 1, 3, 2), "literal even golden vector") + must(transform_map(6, (3,)) == (4, 1, 5, 0, 2, 3), "three-section golden vector") + seen: set[tuple[int, ...]] = set() + count = 0 + for depth, recorded in enumerate(r["schedules"]["by_depth"], start=1): + at_depth = {transform_map(105, schedule) + for schedule in itertools.product((3, 5, 7), repeat=depth)} + seen.update(at_depth) + count += 3**depth + must(recorded["distinct_at_depth"] == len(at_depth), "depth census mismatch") + must(recorded["distinct_up_to_depth"] == len(seen), "cumulative census mismatch") + must(count == r["schedules"]["tested"] == 9840, "schedule coverage mismatch") + must(len(seen) == r["schedules"]["distinct_maps"], "map count mismatch") + for collision in (r["schedules"]["first_collision_examples"] + + r["schedules"]["exact_period_collision_witnesses"]): + p = transform_map(105, tuple(collision["first"])) + q = transform_map(105, tuple(collision["second"])) + must(p == q, "false schedule collision") + must(sha(p) == collision["map_sha256"], "collision map digest mismatch") + for case in r["inversion"]["scale_cases"]: + must(sha(transform_map(case["bits"], (5, 7, 3))) == case["map_sha256"], + "large map mismatch") + for case in r["fixed_map_attack"]["cases"]: + must(case["oracle_queries"] == (case["bits"]-1).bit_length(), "query count") + must(sha(transform_map(case["bits"], (5, 7, 3))) == case["map_sha256"], + "attack inferred map differs from independently constructed map") + orders = {transform_map(105, order) for order in itertools.permutations((3, 5, 7))} + must(len(orders) == 6, "example stage orders unexpectedly coincide") + must(r["full_design"]["classification"] == "BLOCKED_NOT_EXECUTED" and + not r["full_design"]["validated"] and not r["full_design"]["falsified"], + "unsupported whole-system verdict") + out = {"verification": "SURVIVED_SECOND_IMPLEMENTATION", + "same_author": True, "independent_author_review": False, + "imports_primary_runner": False, + "receipt_sha256": hashlib.sha256(path.read_bytes()).hexdigest(), + "verifier_sha256": hashlib.sha256(Path(__file__).read_bytes()).hexdigest(), + "schedules_recomputed": count, "distinct_maps": len(seen), + "distinct_orderings_of_3_5_7": len(orders), + "full_design_verdict": "NOT_EVALUATED"} + print(json.dumps(out, indent=2, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + try: + raise SystemExit(main()) + except (OSError, ValueError, KeyError) as exc: + print(f"VERIFICATION_ERROR: {exc}", file=sys.stderr) + raise SystemExit(1) diff --git a/stack-manifest.json b/stack-manifest.json index 85bdcc57..51f37303 100644 --- a/stack-manifest.json +++ b/stack-manifest.json @@ -1,7 +1,7 @@ { "schema": "the-interdependency.stack-manifest", "version": "1.1.0", - "work_graph_sha256": "1e9a8f485c290bf7e3537e66cc72ac8ced749ce58fd4005647bd3981e071af1f", + "work_graph_sha256": "fa81adcbc37d2042bd6691857f3f97c26f8a16f89821bef66b8331df9553d251", "repositories": [ { "repository": "The-Interdependency/skill-lib", @@ -69,7 +69,8 @@ "ucns has no LICENSE file at snapshot commit 828c0b8", "Python Gonol Construction remains stack-local research without independent repository or release authority", "URPCS is retired historical evidence after GPT-assisted implementation diverged from Erin Spencer's intended multi-arity interleaving specification; why the substitution occurred remains unresolved", - "skill-lib remains a special operational snapshot at stack root rather than following the libs/research pair" + "skill-lib remains a special operational snapshot at stack root rather than following the libs/research pair", + "Weave preserves the intended multi-arity interleaving mechanism, while uneven partitioning, stage-input composition, schedule encoding, thread/corpus binding, hyperspace/gonol binding, authentication/state, and threat model remain unresolved" ] }, "research_participants": [ @@ -190,6 +191,15 @@ "repository": "The-Interdependency/stack", "workspace": "research/urpcs/" }, + { + "authority_transfer": false, + "canonical_release": false, + "commit": "01f340ba5b4c5f107703233750768e201a41da68", + "participant_id": "weave", + "relation": "new stack-local specification and research for Erin Spencer's full intended multi-layer encryption construction: hyperspace/gonol plaintext representation, private-gonol recovery, reconstruction-dependent threads, corpus/material binding, multi-arity bit interleaving, and intended asymmetric key relation; no URPCS implementation inheritance and no security standing yet", + "repository": "The-Interdependency/stack", + "workspace": "research/weave/" + }, { "workspace": "research/english-gonol/", "participant_id": "views-source",