Skip to content

stack-encrypt: bindings send a serialised Planned, and the conformance corpus is written as one #1594

Open

Description

@coderdan

Background

Stack Encrypt (packages/stack-encrypt) is the Rust engine that every CipherStash language SDK runs on. A plan is a record's declaration as data: its fields, each field's context, and each field's indexes. A language binding, such as the Go module, sends the engine a plan and a value, and the engine encrypts, queries, or decrypts.

The Stack Encrypt API plan decided that a value under a plan is one type, Planned. A Planned contains the plan, the caller's context parts, and the value. A Planned is a target, so the owned entry points run it. The markers pull request for #1486 adds Planned to the Rust API, with query plans and the dynamic field handle.

Problem

A binding still sends the engine two frames per operation: a plan frame, then a value frame. Each binding groups them its own way. The Go module calls encrypt_with, query_term, and decrypt_with through the guest, with the plan, the field, the operator, and the value as separate arguments.

So the shape that a binding sends is not the shape the Rust API takes. A conformance case is a plan frame and a value frame, not a serialised Planned. Two bindings can disagree about how the caller's context parts travel, and nothing checks that they agree, because the engine never sees one request.

Proposal

  1. Define the serialised Planned. It has three parts: the plan, by reference or inline; the caller's context parts, in declared order, with their types; and the value. Define the serialised query plan beside it, as {"field": .., "op": ..}. Both are transport forms. A serialised Planned contains plaintext, so the engine reads it from a wiped buffer, and never logs or stores it. The shape is not part of the stored format.
  2. Give the engine one entry point per operation that reads a serialised Planned and answers with the row, the query term, or the plaintext. Make the guest functions in dynamic::record thin wrappers over it.
  3. Make the Go module send serialised Planned values. A Go program generated for rc.3 must run against it. The only change allowed is the one regeneration the handle rename in stack-encrypt refactor: finalize crate ownership, naming and coordinated migration #1489 needs.
  4. Rewrite the conformance corpus so that every case is a serialised Plan or Planned, with the engine's expected answer beside it.

Relationship to other work

Activity

  1. coderdan commented on Nov 9, 2026

    @coderdan
    ContributorAuthor

    Split from #1486. The markers pull request is #1595; it adds Planned, query plans, and the field handle in Rust, and leaves the serialised form, the Go module, and the corpus to this issue.

  2. coderdan commented on Nov 9, 2026

    @coderdan
    ContributorAuthor

    One more acceptance item from #1486 belongs here, with the Go module and the corpus: custom Rust stages that are not registered stay explicitly non-portable, and tests cover the refusal. Pull request #1595 does not cover it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions