Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion conformance/stack-encrypt/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,22 @@ native Rust against the ciphertexts Go has just produced. It never regenerates
expectations. CI also runs the Go cases on its other supported host platforms.
The same task runs the shared declaration acceptance/rejection corpus in both
languages, with native observations of normalization. It also runs the shared
runtime cases described below.
runtime and read cases described below.

The Rust runner is `packages/stack-encrypt/tests/conformance.rs`; the Go runner
is `languages/golang/encrypt/conformance_test.go`. Go calls the record interface
used by generated code. Testing public SDK declarations is a separate layer.
Python and TypeScript runners are pending; their absence is not a passing result.

## `record-reads-v1` contract

The [read contract](read-contract.md) defines 26 cases in `v1/reads.json` for
malformed stored records, authenticated read-type failures and context preflight.
The native runner observes retrieval stages. Go exercises the same cases through
both an uninitialised guest and an active guest, plus the normal record adapter
for representable cases. The runners are `tests/read_conformance.rs` in the Rust
crate and `encrypt/read_conformance_test.go` in the Go module.

## `record-runtime-v1` contract

The [runtime contract](runtime-contract.md) defines 37 cases in `v1/runtime.json`:
Expand Down
20 changes: 19 additions & 1 deletion conformance/stack-encrypt/coverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,30 @@ batches; Go provider-call instrumentation remains open.
| Native source validation tests | `late-wrong-type`, `late-missing`, `unknown-field` | Repeated keys, unsupported frame shapes and more output kinds |
| Existing shared value-kind/operator fixtures | Runtime query cases cover selected conversion edges | Full type/operator matrices, generated SDK calls and verification |

## Read checkpoint

`v1/reads.json` adds 26 cases for stored-shape validation, authenticated types
and context preflight. The [read contract](read-contract.md) distinguishes native
shape/pending stages and the guest's earlier `check_record` diagnostics. Native
data-key counters and Go's uninitialised guest verify preflight boundaries.

| Existing evidence | Shared case IDs | Retained assertions |
|---|---|---|
| Native `a_stored_record_that_does_not_fit` | `missing-field`, `missing-ciphertext`, `field-not-map`, `plaintext-under-c`, `row-not-map`, `late-missing-field` | Repeated keys, other passthrough fields and output kinds |
| Native typed read/null tests | `authenticated-int32-as-*`, `authenticated-null-as-required`, `authenticated-null-as-sql`, `sql-absent-*` | Wider types and declaration combinations |
| Go `context_field_test.go`, native context tests | `context-unrestricted`, `context-expected`, `context-mismatch`, `context-late-mismatch`, `context-drop-tenant`, `context-tenant-*` | Generated APIs, queries, extension composition and stored-context tampering |
| Native and guest context-source validation | `static-context-with-expectation` | Other source combinations and empty-batch behavior |

These are overlap mappings, not grounds for deleting whole mixed-purpose tests.
Repeated-key trees cannot be emitted by Go's map-based tree encoder; a future
raw-byte profile must cover that boundary explicitly.

## Consolidation policy

Shared semantic expectations should be maintained once and executed by each
language runner. Conformance can replace duplicate case tables and fixture
maintenance. It does not eliminate the need to execute the Rust and Go paths.
No existing test has been deleted in these first three slices.
No existing test has been deleted in these first four slices.

| Existing evidence | Relationship to conformance | Consolidation action |
|---|---|---|
Expand Down
105 changes: 105 additions & 0 deletions conformance/stack-encrypt/read-contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Record read profile

`record-reads-v1` adds 26 shared cases in `v1/reads.json` for stored record
shapes, authenticated type checks and context preflight. It fixes the resolver
to `NoTargets`. It is experimental and covers native Rust and Go WASI.

## Inputs and observations

Each case has separate `write` and `read` declarations, each naming the `kind`
and `null` policy of `value`. Outputs are ciphertext and equality. `rows` uses
the runtime profile's typed scalar representation. Runners first encrypt these
rows successfully, then apply the named `mutation` to its zero-based `row`.
This supplies real ciphertext, including for negative reads.

Without `context_field`, the context is `["conformance", "value"]`. With it,
`tenant` is a string passthrough supplying the row context; `value` is its
field identity. An optional `expected_context` constrains the stored context.
Its value must be a nonempty plain label.

`expected` describes the native opener's stage, complete diagnostic code and
payload on refusal, typed ordered rows on success, and data-key retrieval
call count. Runners must reject unknown stages, mutations, capabilities,
profiles and input types. Cases have unique, nonempty IDs.

| Stage | Native observation | Retrieval calls |
|---|---|---|
| `shape` | `record::decrypt` refuses before returning a pending operation | 0 |
| `preflight` | The pending opener refuses before requesting keys | 0 |
| `authenticated` | Shape checking passes; opening the authenticated value finds a type mismatch | 1 |
| `success` | Opened rows match all expected values, types and ordering | 1 when ciphertext is present; 0 for the all-absent SQL-null case |

Counters are reset after fixture encryption. Reads must never generate data
keys. These counts observe native data-key calls, not index-key initialization
or Go's logical provider calls.

`check_error` independently describes `record::check_record`, which needs no
cipher. An absent `check_error` requires acceptance. The Go guest runs this
check before looking up its cipher state. Both an uninitialised guest and an
active guest execute each case: preflight errors must have the specified
diagnostic in either; accepted shapes must reach `ErrState` without a cipher.
Consequently, a concealed type mismatch must pass preflight and fail only when
the active guest opens it.

The Go runner uses a test-only entry point to the existing `se_decrypt_record`
export. This permits shapes the host's `record.Sealed` type cannot express,
including a passthrough directly under `c`. It uses the binding's existing
tree codec, options and transport-buffer cleanup. Cases marked `adapter` also
run the normal `Cipher.Open` path; they retain their original stored shape.
This does not certify generated SDK mappings.

## Required behavior

- Missing required fields, missing ciphertext outputs, non-map rows or field
outputs, and plaintext passthroughs under ciphertext are refused before keys.
- A malformed later row rejects the batch before retrieval.
- Reads project the fields and outputs the plan opens. Extra stored fields,
extra outputs and missing equality outputs do not prevent reading ciphertext.
Writes still reject unknown source fields, as the runtime profile requires.
- Reading int32 ciphertext as int64, uint64 or string fails after retrieval.
Reads do not apply the query path's lossless numeric conversion.
- A sealed null cannot open through a required declaration. It can open through
a SQL-null declaration, with retrieval because ciphertext is present.
- An absent SQL-null field opens as null without retrieval. A required or
sealed-null read declaration refuses the same missing field.
- Context-field reads work without an expected label or with a matching label.
A mismatch, including one in a later row, refuses the whole batch before keys.
- Missing context fields, missing passthrough payloads and non-string context
values fail before retrieval.
- A static-context plan cannot also take a call context. Native `check_record`
and the guest report `dynamic_record` with reason `refused`; the native pending
opener reports `plan_two_context_sources`, naming the plan and the call.
The corpus records both existing diagnostics explicitly.

Context-mismatch diagnostic payloads expose only stored descriptor length and
part count, not its value. Go diagnostic numeric metadata is compared as JSON
numbers; plaintext comparisons retain actual host types.

## Mutation vocabulary

| Mutation | Change to the selected row |
|---|---|
| `none` | Preserve it |
| `drop_value` / `drop_c` / `drop_eq` | Remove the named field or output |
| `field_plain` / `c_plain` / `row_plain` | Replace that node with an int32 passthrough |
| `extra_field` / `extra_output` | Add an `extra` int32 passthrough at that level |
| `drop_tenant` | Remove the context field |
| `tenant_no_payload` | Replace its output map with an empty map |
| `tenant_number` | Replace its passthrough string with an int32 |

## Scope and consolidation

The existing native record tests retain repeated keys, invalid leaf framing,
other tree forms, context extensions and wider type/output combinations. Go's
tree encoder accepts maps with unique keys, so repeated-key transport cases
need a separate raw-byte profile rather than being silently skipped here.

The existing Go context-field suite retains generated SDK calls, query context
selection, extension behavior and stored-context tampering. The deterministic
provider's wrong-context refusal remains covered there and by the scalar
profile. This profile observes preflight context checks, not every context
composition or cryptographic tampering case.

No existing tests are removed. Python and TypeScript runners, EQL/JSON read
shapes, provider failures, version compatibility and runtime artifact exchange
remain follow-up work.
2 changes: 2 additions & 0 deletions conformance/stack-encrypt/runtime-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ guest's lack of network requests is not proof of logical key-call counts.
The 37 cases cover runtime writes, corresponding reads, and equality queries.
They do not cover malformed stored records, authenticated type mismatches on
read, context composition/preflight, provider failures, or other operators.
The separate [read profile](read-contract.md) adds stored-shape, authenticated
type and context-preflight cases.
Fresh cross-language artifact exchange remains the scalar profile's coverage.

Existing `value_kinds.json` and `query_operators.json` fixtures remain the
Expand Down
Loading
Loading