|
| 1 | +# Record read profile |
| 2 | + |
| 3 | +`record-reads-v1` adds 26 shared cases in `v1/reads.json` for stored record |
| 4 | +shapes, authenticated type checks and context preflight. It fixes the resolver |
| 5 | +to `NoTargets`. It is experimental and covers native Rust and Go WASI. |
| 6 | + |
| 7 | +## Inputs and observations |
| 8 | + |
| 9 | +Each case has separate `write` and `read` declarations, each naming the `kind` |
| 10 | +and `null` policy of `value`. Outputs are ciphertext and equality. `rows` uses |
| 11 | +the runtime profile's typed scalar representation. Runners first encrypt these |
| 12 | +rows successfully, then apply the named `mutation` to its zero-based `row`. |
| 13 | +This supplies real ciphertext, including for negative reads. |
| 14 | + |
| 15 | +Without `context_field`, the context is `["conformance", "value"]`. With it, |
| 16 | +`tenant` is a string passthrough supplying the row context; `value` is its |
| 17 | +field identity. An optional `expected_context` constrains the stored context. |
| 18 | +Its value must be a nonempty plain label. |
| 19 | + |
| 20 | +`expected` describes the native opener's stage, complete diagnostic code and |
| 21 | +payload on refusal, typed ordered rows on success, and data-key retrieval |
| 22 | +call count. Runners must reject unknown stages, mutations, capabilities, |
| 23 | +profiles and input types. Cases have unique, nonempty IDs. |
| 24 | + |
| 25 | +| Stage | Native observation | Retrieval calls | |
| 26 | +|---|---|---| |
| 27 | +| `shape` | `record::decrypt` refuses before returning a pending operation | 0 | |
| 28 | +| `preflight` | The pending opener refuses before requesting keys | 0 | |
| 29 | +| `authenticated` | Shape checking passes; opening the authenticated value finds a type mismatch | 1 | |
| 30 | +| `success` | Opened rows match all expected values, types and ordering | 1 when ciphertext is present; 0 for the all-absent SQL-null case | |
| 31 | + |
| 32 | +Counters are reset after fixture encryption. Reads must never generate data |
| 33 | +keys. These counts observe native data-key calls, not index-key initialization |
| 34 | +or Go's logical provider calls. |
| 35 | + |
| 36 | +`check_error` independently describes `record::check_record`, which needs no |
| 37 | +cipher. An absent `check_error` requires acceptance. The Go guest runs this |
| 38 | +check before looking up its cipher state. Both an uninitialised guest and an |
| 39 | +active guest execute each case: preflight errors must have the specified |
| 40 | +diagnostic in either; accepted shapes must reach `ErrState` without a cipher. |
| 41 | +Consequently, a concealed type mismatch must pass preflight and fail only when |
| 42 | +the active guest opens it. |
| 43 | + |
| 44 | +The Go runner uses a test-only entry point to the existing `se_decrypt_record` |
| 45 | +export. This permits shapes the host's `record.Sealed` type cannot express, |
| 46 | +including a passthrough directly under `c`. It uses the binding's existing |
| 47 | +tree codec, options and transport-buffer cleanup. Cases marked `adapter` also |
| 48 | +run the normal `Cipher.Open` path; they retain their original stored shape. |
| 49 | +This does not certify generated SDK mappings. |
| 50 | + |
| 51 | +## Required behavior |
| 52 | + |
| 53 | +- Missing required fields, missing ciphertext outputs, non-map rows or field |
| 54 | + outputs, and plaintext passthroughs under ciphertext are refused before keys. |
| 55 | +- A malformed later row rejects the batch before retrieval. |
| 56 | +- Reads project the fields and outputs the plan opens. Extra stored fields, |
| 57 | + extra outputs and missing equality outputs do not prevent reading ciphertext. |
| 58 | + Writes still reject unknown source fields, as the runtime profile requires. |
| 59 | +- Reading int32 ciphertext as int64, uint64 or string fails after retrieval. |
| 60 | + Reads do not apply the query path's lossless numeric conversion. |
| 61 | +- A sealed null cannot open through a required declaration. It can open through |
| 62 | + a SQL-null declaration, with retrieval because ciphertext is present. |
| 63 | +- An absent SQL-null field opens as null without retrieval. A required or |
| 64 | + sealed-null read declaration refuses the same missing field. |
| 65 | +- Context-field reads work without an expected label or with a matching label. |
| 66 | + A mismatch, including one in a later row, refuses the whole batch before keys. |
| 67 | +- Missing context fields, missing passthrough payloads and non-string context |
| 68 | + values fail before retrieval. |
| 69 | +- A static-context plan cannot also take a call context. Native `check_record` |
| 70 | + and the guest report `dynamic_record` with reason `refused`; the native pending |
| 71 | + opener reports `plan_two_context_sources`, naming the plan and the call. |
| 72 | + The corpus records both existing diagnostics explicitly. |
| 73 | + |
| 74 | +Context-mismatch diagnostic payloads expose only stored descriptor length and |
| 75 | +part count, not its value. Go diagnostic numeric metadata is compared as JSON |
| 76 | +numbers; plaintext comparisons retain actual host types. |
| 77 | + |
| 78 | +## Mutation vocabulary |
| 79 | + |
| 80 | +| Mutation | Change to the selected row | |
| 81 | +|---|---| |
| 82 | +| `none` | Preserve it | |
| 83 | +| `drop_value` / `drop_c` / `drop_eq` | Remove the named field or output | |
| 84 | +| `field_plain` / `c_plain` / `row_plain` | Replace that node with an int32 passthrough | |
| 85 | +| `extra_field` / `extra_output` | Add an `extra` int32 passthrough at that level | |
| 86 | +| `drop_tenant` | Remove the context field | |
| 87 | +| `tenant_no_payload` | Replace its output map with an empty map | |
| 88 | +| `tenant_number` | Replace its passthrough string with an int32 | |
| 89 | + |
| 90 | +## Scope and consolidation |
| 91 | + |
| 92 | +The existing native record tests retain repeated keys, invalid leaf framing, |
| 93 | +other tree forms, context extensions and wider type/output combinations. Go's |
| 94 | +tree encoder accepts maps with unique keys, so repeated-key transport cases |
| 95 | +need a separate raw-byte profile rather than being silently skipped here. |
| 96 | + |
| 97 | +The existing Go context-field suite retains generated SDK calls, query context |
| 98 | +selection, extension behavior and stored-context tampering. The deterministic |
| 99 | +provider's wrong-context refusal remains covered there and by the scalar |
| 100 | +profile. This profile observes preflight context checks, not every context |
| 101 | +composition or cryptographic tampering case. |
| 102 | + |
| 103 | +No existing tests are removed. Python and TypeScript runners, EQL/JSON read |
| 104 | +shapes, provider failures, version compatibility and runtime artifact exchange |
| 105 | +remain follow-up work. |
0 commit comments