stashgen

package
v0.1.0-alpha.1 Latest Latest
Warning

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

Go to latest
Published: Oct 13, 2026 License: MIT Imports: 19 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 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.GeneratedVersion1 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 the JSON family, whose Go names start with JSON (Json is JSON), as Go spells initialisms. 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
}

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.
	Check(ctx context.Context, d Declaration) 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.

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

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
	// 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
}

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.
	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"
)

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
)

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 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