Documentation
¶
Overview ¶
Package scripttoken is the signed token a managed script mints and verifies (#2175): platform.token.mint and platform.token.verify. A token is an HMAC-SHA256 over a JSON body carrying the script's claims, the endpoints or the script it is bound to, its expiry, an id, and whether it may be used once. The key is derived from a stored secret the script names, and the script never sees the secret's value.
It is the same form the bot check's proof-of-work challenge is signed in (internal/botcheck), kept in-house rather than a JWT: the platform both mints and verifies, so nothing outside it has to read one.
platform.decrypt (#2179) is answered here too: like the token members it acts as the run's identity, the persona its author's roles resolve to.
Index ¶
- Constants
- Variables
- func Key(secret string) []byte
- func Reason(err error) string
- func Sign(key []byte, at Binding, m Mint, now time.Time) (string, error)
- type Authority
- type Binding
- type Caller
- type DecryptRequest
- type Decrypter
- type Host
- func (h Host) Decrypt(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, ...) (starlark.Value, error)
- func (h Host) Mint(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, ...) (starlark.Value, error)
- func (h Host) Verify(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, ...) (starlark.Value, error)
- type Issuer
- type KeyReader
- type Mint
- type PostgresUses
- type Token
- type Uses
Constants ¶
const ( CapabilityMint = "platform.mint_token" CapabilityVerify = "platform.verify_token" )
The platform members a script mints and verifies with.
const ( // MaxTokenLen bounds a token before anything is decoded from it. MaxTokenLen = 8192 // MaxClaimsBytes bounds the claims a script puts in one token. MaxClaimsBytes = 4096 // MaxTTL is the longest a token may live. MaxTTL = 30 * 24 * time.Hour // MaxEndpoints is the most endpoints one token may be bound to. MaxEndpoints = 16 )
Limits on what a token carries and how long it lives.
const ( ReasonMalformed = "malformed" ReasonTampered = "tampered" ReasonExpired = "expired" ReasonOtherEndpoint = "other_endpoint" ReasonUsed = "used" )
Reasons a token is refused, as verify returns them to the script.
const CapabilityDecrypt = "platform.decrypt"
CapabilityDecrypt is the member a script reads a form's sensitive fields with (#2179): platform.decrypt(form, payload). It is answered here, beside the token members, because it is bound to the run the same way: the persona the run's author's roles resolve to is what the form's decrypt_personas is checked against.
const Redacted = "[encrypted]"
Redacted is what a recording keeps in place of each value platform.decrypt opened, so no recording, replay or test holds a sensitive value.
const SecretUse = "script_token"
SecretUse is the name a token key's allow_connections lists: a token key is never sent through a connection, so the reserved name is what scopes a stored secret to this use, as botcheck.SecretUse does for a provider key.
Variables ¶
var ErrRefused = errors.New("token refused")
ErrRefused is the error every refusal wraps; Reason reads which it was.
Functions ¶
Types ¶
type Authority ¶
Authority resolves the persona roles act as and whether it is the administrator's, the resolution a run's own session gets from the persona layer.
type Binding ¶
Binding is where a token is minted or verified: inside an endpoint request, the endpoint's name; anywhere else, the script.
type Caller ¶
type Caller interface {
CallTool(ctx context.Context, name string, args map[string]any) (map[string]any, error)
}
Caller answers one mint or verify, named by its capability, with the arguments the script passed: an Issuer in a run, the recording in a test.
type DecryptRequest ¶ added in v1.152.0
type DecryptRequest struct {
Form string
Payload map[string]any
Persona string
Admin bool
At Binding
}
DecryptRequest is one platform.decrypt: the form named, the stored payload handed in, and who the run is.
type Decrypter ¶ added in v1.152.0
type Decrypter interface {
Decrypt(ctx context.Context, req DecryptRequest) (values map[string]any, opened []string, err error)
}
Decrypter opens a form's sensitive fields for a run whose persona the form admits, recording the read.
type Host ¶
type Host struct {
// Ctx is the run's context.
Ctx context.Context //nolint:containedctx // the run's lifetime, which both members read
// Testing is a test or a replay: calls go to Replay.
Testing bool
// Replay answers a test's calls, from the recording or a declared answer.
Replay Caller
// Live answers a run's calls; nil where this deployment issues no tokens.
Live Caller
// Record records a run's call and its answer, nil when nothing records.
Record func(tool string, args, out map[string]any, err error)
}
Host is the two members as a run binds them, the way scriptpace.Clock binds remaining_ms: in a run each call goes to Live and is recorded, and in a test it is answered from the recording, so a test of a handler that mints or verifies needs neither the key nor the database.
func (Host) Decrypt ¶ added in v1.152.0
func (h Host) Decrypt(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error)
Decrypt is platform.decrypt(form, payload): the values of a stored form submission with each sensitive field decrypted. payload is the payload column of webhook_form_<endpoint> as the script read it: the dict platform.query returns, or its JSON text. form is the form's endpoint name.
func (Host) Mint ¶
func (h Host) Mint(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error)
Mint is platform.mint_token(claims, ttl, secret, endpoints=None, single_use=False): the token, as a string.
func (Host) Verify ¶
func (h Host) Verify(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error)
Verify is platform.verify_token(token, secret): a dict whose "ok" says whether the token holds, with its "claims" when it does and the "reason" it was refused for when it does not, so a script answers a refusal itself.
type Issuer ¶
type Issuer struct {
Keys KeyReader
Uses Uses
// Authority resolves a run's roles to its persona; nil resolves none, so
// only a key naming no personas can be used.
Authority Authority
// Now is the clock; nil is time.Now.
Now func() time.Time
// contains filtered or unexported fields
}
Issuer answers a run's mints and verifies: the deployment's secrets and single-use record, and Bind's per-run identity.
func (*Issuer) Bind ¶
Bind is the Caller for one run: where it mints and verifies, and the roles the run presents, whose persona a key's allow_personas is checked against. A nil Issuer binds nothing, so a deployment without one leaves the members saying so.
func (*Issuer) SetDecrypter ¶ added in v1.152.0
SetDecrypter is what answers platform.decrypt from here on: the form sources, assembled after the issuer. Nil answers it with a refusal.
type KeyReader ¶
KeyReader reads a stored secret's value for this use, refusing a secret whose allow_connections does not list SecretUse or whose allow_personas does not name persona (the administrator's is unrestricted).
type Mint ¶
type Mint struct {
Claims map[string]any
TTL time.Duration
// Endpoints names the endpoints the token verifies at; empty binds it to
// where it was minted.
Endpoints []string
SingleUse bool
}
Mint is one token's request.
type PostgresUses ¶
type PostgresUses struct {
// contains filtered or unexported fields
}
PostgresUses is Uses over the script_token_uses table.
type Uses ¶
type Uses interface {
// Use records the token's id until it expires and reports whether this
// was its first use.
Use(ctx context.Context, id string, expires time.Time) (bool, error)
// Sweep deletes the records of tokens that have expired.
Sweep(ctx context.Context, now time.Time) (int64, error)
}
Uses records the single-use tokens that have been verified, so a second use is refused on every replica.