Documentation
¶
Overview ¶
Package contract holds the parts of the serverless endpoint contract that are not protobuf messages: header names, the upgrade signature payload, wire constants and the protojson encoding every message travels in. The messages themselves are generated from api-contracts/v1/serverless.proto into internal/services/shared/proto/v1, which is also the source of the endpoint SDK's TypeScript types.
Package contract holds the constants shared between the serverless operator core and the API server: the data ids for secrets encrypted at rest. Both sides must agree on these for ciphertext written by one to be readable by the other.
Index ¶
- Constants
- func Marshal(m proto.Message) ([]byte, error)
- func MarshalFrame(frame *v1.ServerlessDurableFrame) ([]byte, error)
- func Sign(data string, secret string) (string, error)
- func Unmarshal(raw []byte, m proto.Message) error
- func UnmarshalFrame(raw []byte) (*v1.ServerlessDurableFrame, error)
- func UpgradeSigningPayload(endpointId, timestamp, nonce, taskId, invocation string) string
- func Verify(data string, secret string, signature string) bool
Constants ¶
const ( SignatureHeader = "X-Hatchet-Signature" EndpointIdHeader = "X-Hatchet-Endpoint-Id" // TimestampHeader carries the time the request was built as decimal Unix seconds (not // milliseconds or nanoseconds), the same value as the body's timestamp_unix_seconds. An // endpoint rejects a value more than RequestMaxAge (5 minutes) from its own clock in // either direction. TimestampHeader = "X-Hatchet-Timestamp" )
Headers carried by every request the operator sends to an endpoint. Endpoints verify SignatureHeader by recomputing the HMAC-SHA256 hex digest of the raw body with their signing secret (Verify). The signed body of every POST (v1.ServerlessHealthcheckRequest, v1.ServerlessTriggerRequest) carries the operator's timestamp in Unix seconds; an endpoint rejects a body whose timestamp lies more than RequestMaxAge from its own clock in either direction, so a captured request cannot be replayed later. Within that window a delivery may be repeated: endpoints key side effects on (endpoint id, task run external id, retry count).
const ( NonceHeader = "X-Hatchet-Nonce" TaskIdHeader = "X-Hatchet-Task-Id" InvocationHeader = "X-Hatchet-Invocation" )
Headers carried only by the durable websocket upgrade, which has no body to sign. The endpoint verifies SignatureHeader against UpgradeSigningPayload with its signing secret, rejects a TimestampHeader more than UpgradeMaxAge from its clock in either direction, consumes NonceHeader from a nonce set after the signature verified so a captured upgrade cannot be replayed within the window (every accepted nonce is kept until its window has passed; an endpoint whose nonce storage cannot admit another refuses the upgrade rather than forget a live nonce), and checks that the first frame's task id and invocation are the ones the headers were signed for.
const ( // ErrorCodeNonDeterminism reports a replay that diverged from the log. ErrorCodeNonDeterminism = "nondeterminism" // ErrorCodeUnspecified is every other engine-side error. ErrorCodeUnspecified = "unspecified" )
Codes of v1.ServerlessErrorFrame.
const DoneStatusEvicted = "evicted"
DoneStatusEvicted is the v1.ServerlessDoneFrame status of an invocation that evicted itself.
const RequestMaxAge = 5 * time.Minute
RequestMaxAge is how far a signed timestamp may lie from the endpoint's clock, in either direction, before the endpoint rejects the request. It applies to the timestamp inside every signed POST body and to the upgrade's TimestampHeader.
const SigningSecretEncryptionDataID = "v1_serverless_endpoint_signing_secret" //nolint:gosec // associated-data label for envelope encryption, not a credential
SigningSecretEncryptionDataID is the associated data used when encrypting a serverless endpoint's signing secret with pkg/encryption. It binds the ciphertext in v1_serverless_endpoint.signing_secret_enc to that column so it cannot be replayed as any other secret.
const TriggerEnvelopeVersion = 1
TriggerEnvelopeVersion is the version field of v1.ServerlessTriggerRequest.
const UpgradeMaxAge = RequestMaxAge
UpgradeMaxAge is RequestMaxAge as it applies to the durable upgrade.
Variables ¶
This section is empty.
Functions ¶
func MarshalFrame ¶
func MarshalFrame(frame *v1.ServerlessDurableFrame) ([]byte, error)
MarshalFrame encodes one durable websocket frame.
func Sign ¶
Sign returns the hex-encoded HMAC-SHA256 of data under secret, the value carried in SignatureHeader. The key is checked against the FIPS minimum length first, so a short secret is an error instead of a panic.
func UnmarshalFrame ¶
func UnmarshalFrame(raw []byte) (*v1.ServerlessDurableFrame, error)
UnmarshalFrame decodes one durable websocket frame.
func UpgradeSigningPayload ¶
UpgradeSigningPayload is the string the durable upgrade signature covers: endpoint_id "." timestamp "." nonce "." task_id "." invocation, each as it appears in its header. The endpoint id is part of it so a signature made for one endpoint cannot be presented to another that shares the signing secret: the endpoint verifies the id it serves against the signed one, not against an unsigned header alone.
Types ¶
This section is empty.