stashgen

package
v0.1.0-rc.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 2, 2026 License: MIT Imports: 21 Imported by: 1

Documentation

Overview

Package stashgen is the Stack Encrypt code generator for Go, as a library.

A struct's stash tags declare how each field is encrypted. FromTags reads those tags through go/packages and go/types, checks the declaration with an Engine, and returns the file that the command stashgen writes beside the struct: the encrypted type, Encrypt, Decrypt, Fields, Paths (for a JSON document whose arrays have ordered members) and the print methods. The command at languages/golang/cmd/stashgen is the go:generate front end for this package.

The generator reads types, not text, and runs none of the package's code. It ignores its own output file when it loads the package, so a stale generated file does not stop it. The same input always gives the same file: fields keep their declared order, and the file carries no version and no time beyond the gensupport.GeneratedVersion2 constant it names.

Generate writes the same file for a type that cannot carry tags, from a policy in package encrypt/policy: a source gives the facts about each field and the policy's rules decide them. A decision spells itself in the tag grammar, so both paths go through one parser and one emitter.

Every refusal about an index, an EQL type or a field type comes from the Engine. The generator holds no copy of the engine's rules.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func EQLGoName

func EQLGoName(name string) string

EQLGoName is the Go type name in encrypt/eql of an EQL type the engine names: the same name in every language, save that the word Json is JSON, as Go spells initialisms (Json is JSON, JsonSearchQuery is JSONSearchQuery; Json is a word where the name ends or the next letter is upper case). The generated encrypt/eql package follows the same rule (eql-codegen's go_eql renderer), and its Types table records both names, which is what holds the two in agreement (a test in cmd/stashgen reads it; this package's own tests cannot link encrypt/eql without changing the engine they test).

func Generate

func Generate(ctx context.Context, source policy.Source, message policy.Message, output string, opts ...GenerateOption) error

Generate writes the generated file for a message from a policy to output: the source gives the facts about each field, the message's rules decide each one, and the file is the same one stashgen writes from tags. The generated functions take and return pointers to the message. output goes in a package of your own, not in the package of the generated type, so the generate program never imports a file that it wrote.

Every field of the message needs a decision. A field that no rule decides stops the generator with the field's name and its annotations, and so does a field that a rule refuses with Fail.

Types

type Declaration

type Declaration struct {
	// Type is the Go type the declaration is for, as the generator prints it
	// in an error: "User" or "crm.Contact".
	Type string
	// Context is the context of every field, from the `context=` tag; ""
	// when ContextField is set.
	Context string
	// ContextField is the declaration name of the field whose value is the
	// context of every other field, from the `context_field` tag; "" when
	// the context comes from Context. The field is a string, stored as it
	// is, in the clear and unauthenticated like a passthrough.
	ContextField string
	// Opaque seals the whole struct as one value. The fields then carry no
	// tags and no indexes.
	Opaque bool
	// Fields are the struct's fields in declared order, omitted ones included.
	Fields []Field
}

Declaration is how each field of one struct is encrypted: the context of the struct, and the verb, indexes and EQL type of each field. The generator builds it from the stash tags, the engine checks it, and the generated file holds it as data that only generated code uses.

type EQLType

type EQLType struct {
	// Name is the Go type name in encrypt/eql, and the value of encrypt_into.
	Name string
	// Plaintext is the kind of Go value the type seals.
	Plaintext Kind
	// Indexes are the terms the type carries.
	Indexes []IndexName
	// Query is the Go type name of the type's query value, such as
	// TextEqQuery, or "" for a type with no terms.
	Query string
	// Producible says whether this build of the engine produces the type;
	// Reason says why not when it does not.
	Producible bool
	Reason     string
	// contains filtered or unexported fields
}

EQLType is one EQL type the engine produces, such as TextEq.

type Engine

type Engine interface {
	// EQLTypes lists the EQL types the engine can produce: each name, its
	// plaintext kind, its indexes and its query type.
	EQLTypes(ctx context.Context) ([]EQLType, error)
	// Check refuses a declaration the engine cannot run: an index or an EQL
	// type that does not apply to a field's Go type, a field type it cannot
	// seal, an EQL type it cannot produce, or an index option it cannot
	// carry. The error names the field. For a declaration it accepts, it
	// lists the operators the engine answers on each sealed field, by the
	// field's declared name, in the engine's order: the generator writes one
	// query method for each.
	Check(ctx context.Context, d Declaration) (map[string][]Operator, error)
}

Engine answers the generator's questions about what the Rust engine can do. The generator holds no copy of the engine's rules: every refusal about an index, an EQL type or a field type comes from here, and so does every operator a field's query methods name.

GuestEngine is the SDK's implementation: the WASI guest that package encrypt embeds, asked through se_plan_check, se_targets and se_operators.

func GuestEngine

func GuestEngine(ctx context.Context) (Engine, error)

GuestEngine is the engine the SDK embeds: the WASI guest in package encrypt, asked through its se_plan_check and se_targets exports. It holds no credentials and makes no request. Close it when done.

type Field

type Field struct {
	// Name is the field's name in the declaration, which is its column name.
	// An omitted field's Name is its Go name in snake case.
	Name string
	// GoName is the field's name in the Go struct.
	GoName string
	// GoType is the field's Go type, for the engine's checks.
	GoType GoType
	// Verb says what happens to the field.
	Verb Verb
	// Indexes are the indexes the tag names, for EncryptIndex and Index.
	Indexes []Index
	// EQLType is the EQL type an EncryptInto field seals into.
	EQLType string
	// TargetOptions are the EQL type's options, for a type that takes them
	// (JsonSearch); nil for its defaults.
	TargetOptions *record.TargetOptions
	// Identity is the field's part of its context when it differs from Name:
	// a column that was renamed keeps the identity it was first written
	// under. "" means Name. Only a policy sets it.
	Identity string
	// Null is how a sealed field declared over a pointer stores nil; NotNull
	// for every other field. GoType is then the type the pointer points to.
	Null Null
}

Field is one field of a declaration.

func (Field) HasCiphertext

func (f Field) HasCiphertext() bool

HasCiphertext reports whether the field's outputs include a ciphertext.

func (Field) Sealed

func (f Field) Sealed() bool

Sealed reports whether the field has a ciphertext or a term: every verb but omit and passthrough.

func (Field) Stored

func (f Field) Stored() bool

Stored reports whether the field is stored as it is: a passthrough, or the context field.

type FieldError

type FieldError struct {
	// Type is the Go type the field belongs to: "User" or "crm.Contact".
	Type string
	// Field is the Go name of the field, or "" for a mistake about the type.
	Field string
	// Reason says what is wrong, without the type and field.
	Reason string
}

FieldError is a mistake in one field's declaration. Every refusal names the type and the field, and never a value.

func (*FieldError) Error

func (e *FieldError) Error() string

type File

type File struct {
	// Path is where the file goes.
	Path string
	// Content is the formatted Go source.
	Content []byte
	// Notices are what the generator prints to stderr: the unexported fields
	// it ignored, and a type that prints its sealed fields.
	Notices []string
}

File is a generated file, ready to write.

func FromTags

func FromTags(ctx context.Context, engine Engine, req Request) (*File, error)

FromTags generates the file for the struct named in the request. It loads the package with go/packages and reads types, not text; it runs none of the package's code; it checks the declaration with the engine; and it returns the file without writing it. The same request always gives the same file.

func (*File) Write

func (f *File) Write() error

Write writes the file. Generated source is committed and read by everyone who builds the package, so it gets the mode of any other source file.

type GenerateOption

type GenerateOption func(*generateConfig)

GenerateOption configures Generate.

func WithEngine

func WithEngine(e Engine) GenerateOption

WithEngine checks the declaration with this engine instead of the one the SDK embeds.

func WithName

func WithName(name string) GenerateOption

WithName gives the generated names a prefix, as -name does on the tag path: WithName("Individual") writes EncryptIndividual, DecryptIndividual and IndividualFields. A package holds one Encrypt, so the second message generated into one package needs a name.

func WithNotices

func WithNotices(w io.Writer) GenerateOption

WithNotices sends the generator's notices here instead of stderr.

type GoType

type GoType struct {
	// Name is the type as Go source in the field's package: "string",
	// "int64", "[]string", "time.Time", "crm.Address".
	Name string
	// Kind is the type's underlying kind.
	Kind Kind
	// Basic is the underlying basic type's name for a scalar kind — "uint8",
	// "int", "string" — which decides the wire type the field seals as. It is
	// "" for a value type the generator knows by name (time.Time,
	// encrypt.Date, encrypt.Int128, encrypt.Uint128, encrypt.Decimal), whose
	// kind says all there is.
	Basic string
	// Elem is the element type of a slice, map or pointer, and nil otherwise.
	Elem *GoType
	// Fields are the fields of a struct kind, in declared order.
	Fields []GoType
}

GoType describes a field's Go type to the engine without go/types.

func (GoType) String

func (t GoType) String() string

String returns the type as Go source.

type Index

type Index struct {
	Name    IndexName
	Options []Option
}

Index is one index on a field, with its options.

func (Index) String

func (i Index) String() string

String spells the index as the tag does: `match` or `match(k=3)`.

type IndexName

type IndexName string

IndexName is the tag word for an index. The words are the Rust API's.

const (
	IndexEquality IndexName = "equality"
	IndexMatch    IndexName = "match"
	IndexOre      IndexName = "ore"
	IndexOpe      IndexName = "ope"
	IndexJSON     IndexName = "json"
	// IndexOreBlock is block ORE (ore-rs's 6-bit schemes): the index of an
	// EQL type's ORE term (`ob`), TextOrdOre's and IntegerOrdOre's. It is
	// not a tag word: a field reaches it by naming such an EQL type
	// (encrypt_into=), and indexNames leaves it out.
	IndexOreBlock IndexName = "ore_block"
)

The index names.

func (IndexName) GoName

func (n IndexName) GoName() string

GoName is the index's name in generated code: the field of the output struct that holds its term, and the query method of the field entry.

type Kind

type Kind uint8

Kind is the underlying kind of a Go type, as far as the engine needs it.

const (
	// KindOther is a kind the engine cannot seal: a channel, a function, an
	// interface, a complex number, or a struct with an unexported field.
	KindOther Kind = iota
	KindString
	KindBool
	KindInt
	KindUint
	KindFloat
	// KindBytes is []byte.
	KindBytes
	KindSlice
	KindMap
	KindPointer
	// KindStruct is a struct whose fields are all exported.
	KindStruct
	// KindInt128 and KindUint128 are encrypt.Int128 and encrypt.Uint128.
	KindInt128
	KindUint128
	// KindDate is encrypt.Date, a calendar date.
	KindDate
	// KindTimestamp is time.Time, an instant.
	KindTimestamp
	// KindDecimal is encrypt.Decimal.
	KindDecimal
	// KindJSON is json.RawMessage: a JSON document, stored as an
	// JSONSearch value.
	KindJSON
	// KindJSONValue is a struct, map or slice type stored as an
	// JSONSearch document: encoding/json writes it to JSON text and reads
	// it back. A field is classified so only where it names such an EQL
	// type; anywhere else the type keeps its own kind.
	KindJSONValue
)

func (Kind) Scalar

func (k Kind) Scalar() bool

Scalar reports whether the kind is one value an index can take.

func (Kind) String

func (k Kind) String() string

String returns the kind's name.

type ModelRequest

type ModelRequest struct {
	// Name is the suffix of the generated functions: EncryptName, DecryptName.
	Name string
	// Type is the model struct, in this package or as P.T.
	Type string
	// Declares is a struct in this package that carries the tags for a Type
	// that cannot, or "".
	Declares string
}

ModelRequest is one -model flag: a struct with one field for each column, for separate columns.

func ParseModelFlag

func ParseModelFlag(s string) (ModelRequest, error)

ParseModelFlag reads a -model flag: Name=R or Name=R:D.

type Null

type Null string

Null is how a sealed field declared over a pointer (*T) stores nil: the `null=` tag, or the generator's choice where the tag is left out.

const (
	// NotNull is a field that is not a pointer: it is never nil.
	NotNull Null = ""
	// NullSealed seals nil like a value, as long as one, so the stored
	// field does not show that the value is absent. Its equality term is the
	// one every nil shares, and the generated EqNull queries it. The default
	// for a field whose only index, if any, is equality and whose type has a
	// fixed width: a bool, a number, encrypt.Date, time.Time,
	// encrypt.Decimal, encrypt.Int128 or encrypt.Uint128.
	NullSealed Null = "sealed"
	// NullSQL stores nothing for nil: the field's columns hold SQL NULL,
	// which shows that the value is absent. A field with an index other than
	// equality, or of text or bytes, says so with `null=sql`: its absence
	// would show anyway.
	NullSQL Null = "sql"
)

type Operator

type Operator struct {
	// Name is the operator's name, as a query names it: eq, lt, lte, gt,
	// gte, between or matches.
	Name string
	// Probabilistic says the server's answer can hold false positives, so
	// the query's type carries Verify and TargetLimit; an exact answer's
	// carries neither.
	Probabilistic bool
	// Pair says the operator takes two values, low then high, as between
	// does.
	Pair bool
	// Index is the index that answers it: the field's own, or its EQL
	// type's.
	Index IndexName
}

Operator is one operator the engine answers on a field, such as eq or matches: the generated method's name, its input and its result follow from it.

type Option

type Option struct {
	Key   string
	Value string
}

Option is one option of an index, from the parentheses after its name.

func (Option) String

func (o Option) String() string

String spells the option as the tag does.

type Request

type Request struct {
	// Dir is the directory of the package. "" is the working directory.
	Dir string
	// Type is the struct that carries the stash tags.
	Type string
	// Name, when set, writes EncryptName, DecryptName and NameFields.
	Name string
	// For, as P.F, is the type in another package that Type declares for.
	For string
	// Models are the -model flags.
	Models []ModelRequest
	// Redact writes String and LogValue methods on Type.
	Redact bool
	// Output is the file to write, relative to Dir. "" is the type's name in
	// lower case with _stash.go.
	Output string
}

Request is one run of the generator over one tagged struct. The command fills it from its flags.

func (Request) OutputPath

func (r Request) OutputPath() string

OutputPath is the file the request writes, relative to Dir.

type Verb

type Verb uint8

Verb is what happens to a field.

const (
	// VerbOmit leaves the field out: it does not cross the binding.
	VerbOmit Verb = iota
	// VerbPassthrough stores the field as it is.
	VerbPassthrough
	// VerbEncrypt seals the field with no index.
	VerbEncrypt
	// VerbEncryptIndex seals the field and derives each index beside it.
	VerbEncryptIndex
	// VerbIndex derives the indexes alone, with no ciphertext.
	VerbIndex
	// VerbEncryptInto seals the field into one EQL value.
	VerbEncryptInto
	// VerbContextField stores the field as it is, and its value is the
	// context every other field is sealed under.
	VerbContextField
)

func (Verb) String

func (v Verb) String() string

String returns the tag word for the verb.

Directories

Path Synopsis
Package enginetest is a static stand-in for the Rust engine in the generator's tests.
Package enginetest is a static stand-in for the Rust engine in the generator's tests.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL