space

package
v0.14.1 Latest Latest
Warning

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

Go to latest
Published: Oct 30, 2026 License: MIT Imports: 25 Imported by: 0

Documentation

Overview

Package space implements the primitives of ATProto Spaces (permissioned data), following @atproto/space in bluesky-social/atproto PR #5187 at 5b95b2f2: the set hash, signed space repo commits, space tokens, HTTP message signatures and the space repo CAR format.

Index

Constants

View Source
const (

	// CredentialScheme is the Authorization scheme a space credential is
	// presented with.
	CredentialScheme = "Atproto-Space-Credential"
	// HeaderSpaceAudience names the audience DID a credential request is for.
	HeaderSpaceAudience = "Atproto-Space-Audience"
)
View Source
const (
	CommitMacType    = "atproto-space-repo"
	RecordMacType    = "atproto-space-record"
	RecordMacVersion = 1

	// HeaderWantMac is the request header asking for a MAC.
	HeaderWantMac = "Atproto-Space-Want-Mac"
	// HeaderMac is the response header carrying it.
	HeaderMac = "Atproto-Space-Mac"
)
View Source
const (
	// ClockSkewSec is the allowance applied to exp, and to a credential's iat.
	ClockSkewSec = 5
	// CredentialMaxAgeSec bounds a credential's lifetime.
	CredentialMaxAgeSec = 3600
)
View Source
const CommitVersion = 1

CommitVersion is the only commit version this package understands.

View Source
const (

	// LtHashStateBytes is the size of a persisted set hash state.
	LtHashStateBytes = ltHashLanes * 2
)
View Source
const MaxRecordSize = 1024 * 1024

MaxRecordSize is the largest DRISL encoding a space record may have.

View Source
const RepoStarType = "atproto-space-repo"

RepoStarType is the $type of a space repo archive's metadata.

Variables

View Source
var StarMagic = []byte{0x2a, 0x6c, 0x01, 0x00}

StarMagic opens every STAR archive.

View Source
var TokenSpecs = map[TokenType]TokenSpec{
	TokenDelegation:        {Typ: "atproto-space-delegation+jwt", Kid: "#atproto", ExpiresInSec: 60, RequireAud: true, SingleUse: true},
	TokenCredential:        {Typ: "atproto-space-credential+jwt", Kid: "#atproto", ExpiresInSec: 600, RequireCnf: true},
	TokenClientAttestation: {Typ: "atproto-client-attestation+jwt", ExpiresInSec: 60, RequireAud: true, SingleUse: true},
}

TokenSpecs follows SPACE_TOKEN_TYPES in @atproto/space. Credentials carry no audience: HTTP message signatures bind each use to an audience DID.

Functions

func ComputeCommitMac added in v0.14.1

func ComputeCommitMac(sharedSecret []byte, c Commit) []byte

ComputeCommitMac is

context = "atproto-space-repo-v1"
   || uint16be(len(space)) || space
   || uint16be(len(repo))  || repo
   || uint16be(len(rev))   || rev
mac = HMAC-SHA256(shared_secret, context || data)

func ComputeRecordMac added in v0.14.1

func ComputeRecordMac(sharedSecret []byte, uri string, c cid.Cid) []byte

ComputeRecordMac is

context = "atproto-space-record-v1" || uint16be(len(uri)) || uri
mac = HMAC-SHA256(shared_secret, context || record_cid_bytes)

func CreateSpaceSig added in v0.13.1

func CreateSpaceSig(key atcrypto.PrivateKey, authorization, audience string) (string, []byte, error)

CreateSpaceSig signs a request authorized by a space credential, covering the credential (authorization, which must use the Atproto-Space-Credential scheme) and its audience DID. It returns the signature-input inner list and the compact signature.

func CreateSpaceSigHeaders added in v0.13.1

func CreateSpaceSigHeaders(key atcrypto.PrivateKey, authorization, audience string) (map[string]string, error)

CreateSpaceSigHeaders returns the authorization, audience and signature headers for a space request, keyed by lowercase header name.

func CreateSpaceToken

func CreateSpaceToken(typ TokenType, opts CreateTokenOpts, key atcrypto.PrivateKey) (string, error)

CreateSpaceToken mints and signs a space token.

func Decapsulate added in v0.14.1

func Decapsulate(authorKem *atcrypto.PrivateKeyP256, keyShare string) ([]byte, error)

Decapsulate derives, on the repo host, the shared secret for a syncer's key_share using the author's KEM private key.

func FormatMacHeader added in v0.14.1

func FormatMacHeader(typ string, mac []byte) string

FormatMacHeader is the Atproto-Space-Mac response header, e.g. type="atproto-space-repo", ver=1, mac=:AAECAwQF...:.

func FormatRecordPath added in v0.13.1

func FormatRecordPath(collection, rkey string) string

FormatRecordPath is "{collection}/{rkey}".

func FormatSetHashElement

func FormatSetHashElement(collection, rkey string, c cid.Cid) string

FormatSetHashElement is the element a record contributes to its repo's set hash: "{collection}/{rkey}/{cid}". It stays injective because the outer two fields (an NSID and a base32 CID) never contain a slash.

func FormatWantMacHeader added in v0.14.1

func FormatWantMacHeader(typ, keyShare string) string

FormatWantMacHeader is the Atproto-Space-Want-Mac request header, e.g. type="atproto-space-repo", ver=1, key_share="did:key:zDna...".

func IsP256DIDKey added in v0.14.1

func IsP256DIDKey(didKey string) bool

IsP256DIDKey reports whether didKey is a P-256 did:key, the only kind a space credential can be bound to.

func JWTAlg added in v0.13.1

func JWTAlg(key any) (string, error)

JWTAlg is the JWT alg for a key: ES256 for P-256, ES256K for secp256k1.

func ParseMacHeader added in v0.14.1

func ParseMacHeader(values []string, typ string) ([]byte, error)

ParseMacHeader parses a response header of the given type, returning its MAC.

func ParseRecordPath added in v0.13.1

func ParseRecordPath(p string) (string, string, error)

ParseRecordPath splits "{collection}/{rkey}".

func ParseWantMacHeader added in v0.14.1

func ParseWantMacHeader(values []string, typ string) (string, error)

ParseWantMacHeader parses a request header of the given type, returning its key_share. values are every field line of the header.

func RepoStarMeta added in v0.14.1

func RepoStarMeta(c Commit) map[string]any

RepoStarMeta is a commit as a repo archive's metadata.

func SerializeRepo

func SerializeRepo(w io.Writer, commit Commit, records []SerializedRecord) error

SerializeRepo writes a space repo archive. Records must be in strict path order, as the archive requires; it fails at the first that isn't.

func SpaceHostAud added in v0.13.1

func SpaceHostAud(authority string) string

SpaceHostAud is the audience a delegation token and a client attestation name: the authority acting as space host, which is its PDS like any other atproto account.

func VerifyCommit

func VerifyCommit(c Commit, p VerifyCommitParams) bool

VerifyCommit checks a commit against the MAC its response carried. Once it passes, Data is trusted as the author's claim about their repo, which is what makes Matches meaningful.

func VerifyCommitMac added in v0.14.1

func VerifyCommitMac(sharedSecret []byte, c Commit, mac []byte) bool

VerifyCommitMac checks a commit MAC in constant time.

func VerifyCredentialSig added in v0.14.1

func VerifyCredentialSig(h http.Header, keyID string) error

VerifyCredentialSig verifies the signature on a request authorized by a space credential, against the key the credential is bound to (its cnf.kid).

func VerifyJWTSignature added in v0.13.1

func VerifyJWTSignature(pub atcrypto.PublicKey, alg string, input, sig []byte) (bool, error)

VerifyJWTSignature checks a compact, low-S JWT signature, refusing an alg that does not match the key.

func VerifyRecord added in v0.14.1

func VerifyRecord(uri, cidStr string, value map[string]any, p VerifyRecordParams) bool

VerifyRecord checks a getRecord response against the MAC it carried: it must be the record requested, the MAC must cover its uri and cid, and its value must encode to that cid.

func VerifyRecordMac added in v0.14.1

func VerifyRecordMac(sharedSecret []byte, uri string, c cid.Cid, mac []byte) bool

VerifyRecordMac checks a record MAC in constant time.

Types

type Commit added in v0.14.1

type Commit struct {
	Ver   int64
	Repo  string
	Space string
	Rev   string
	// Data is the sha256 digest of the repo's LtHash state.
	Data []byte
}

Commit is a space repo commit (com.atproto.space.defs#commit). It carries no signature: the repo host authenticates it to each syncer with a MAC in the Atproto-Space-Mac response header, so a leaked commit proves nothing to a third party about what the author wrote.

func ParseRepoStarMeta added in v0.14.1

func ParseRepoStarMeta(m map[string]any) (Commit, error)

ParseRepoStarMeta reads a commit from a repo archive's metadata.

func (Commit) MarshalJSON added in v0.14.1

func (c Commit) MarshalJSON() ([]byte, error)

MarshalJSON encodes a commit as com.atproto.space.defs#commit.

func (*Commit) UnmarshalJSON added in v0.14.1

func (c *Commit) UnmarshalJSON(raw []byte) error

type CommitCtx

type CommitCtx struct {
	Space string `json:"space"`
	Repo  string `json:"repo"`
	Rev   string `json:"rev"`
}

CommitCtx is which repo, in which space, at which revision a commit describes.

type CreateTokenOpts added in v0.13.1

type CreateTokenOpts struct {
	Iss string
	Sub string
	Aud string
	// KeyID is the did:key the credential is bound to (cnf.kid).
	KeyID string
	// ExpiresInSec overrides the type's default lifetime.
	ExpiresInSec *int64
	// Kid is the header kid, only for a client attestation. Other token types
	// always use #atproto.
	Kid string
	// Now overrides the clock.
	Now time.Time
}

type Encapsulation added in v0.14.1

type Encapsulation struct {
	KeyShare     string
	SharedSecret []byte
}

Encapsulation is a syncer's side of one request: the ephemeral public key to send as key_share, and the secret it shares with the repo host.

func Encapsulate added in v0.14.1

func Encapsulate(authorKemKey string) (*Encapsulation, error)

Encapsulate generates a fresh ephemeral keypair and derives a shared secret against the author's #atproto_kem key (a P-256 did:key). Use a new one for every request.

type LexBytes added in v0.13.1

type LexBytes []byte

LexBytes is the atproto JSON encoding of bytes: {"$bytes": base64}.

func (LexBytes) MarshalJSON added in v0.13.1

func (b LexBytes) MarshalJSON() ([]byte, error)

func (*LexBytes) UnmarshalJSON added in v0.13.1

func (b *LexBytes) UnmarshalJSON(raw []byte) error

type LtHash

type LtHash struct {
	// contains filtered or unexported fields
}

LtHash is a homomorphic set hash. Each element expands (BLAKE3 XOF) to 1024 little-endian u16 lanes, which are summed into the state mod 2^16, so the state depends only on the current multiset, not on insertion order.

func LtHashFromState

func LtHashFromState(state []byte) (*LtHash, error)

LtHashFromState resumes a set hash from a persisted state. An empty state yields the empty hash.

func NewLtHash

func NewLtHash() *LtHash

NewLtHash returns the empty set hash.

func (*LtHash) Add

func (h *LtHash) Add(element string) *LtHash

Add folds an element into the set.

func (*LtHash) Clone added in v0.13.1

func (h *LtHash) Clone() *LtHash

Clone returns an independent copy.

func (*LtHash) Digest

func (h *LtHash) Digest() [32]byte

Digest is sha256 over the state.

func (*LtHash) Equal

func (h *LtHash) Equal(o *LtHash) bool

Equal compares two states.

func (*LtHash) IsEmpty

func (h *LtHash) IsEmpty() bool

IsEmpty reports whether the state is all zeros.

func (*LtHash) Remove

func (h *LtHash) Remove(element string) *LtHash

Remove takes an element out of the set.

func (*LtHash) State

func (h *LtHash) State() []byte

State returns a copy of the full state, for persistence.

type MacError added in v0.14.1

type MacError struct {
	Message string
	Cause   error
}

MacError is a missing or malformed MAC header, an unsupported MAC type or version, or an invalid key (BadSpaceMac).

func (*MacError) Error added in v0.14.1

func (e *MacError) Error() string

func (*MacError) Unwrap added in v0.14.1

func (e *MacError) Unwrap() error

type ParsedToken added in v0.13.1

type ParsedToken struct {
	Token
	SigningInput []byte
	Sig          []byte
}

ParsedToken is a structurally valid token with its signing input, before any signature check.

func ParseSpaceToken

func ParseSpaceToken(typ TokenType, jwt string) (*ParsedToken, error)

ParseSpaceToken validates a token's structure, without checking its signature. That is as far as a client attestation goes here, since its key comes from the client's JWKS rather than a DID document.

type RecordRef

type RecordRef struct {
	Collection string
	Rkey       string
	Cid        cid.Cid
}

RecordRef names a record at a CID.

type Ref added in v0.13.1

type Ref struct {
	Authority string
	Type      string
	Skey      string
}

Ref names a space: at://{authority}/space/{spaceType}/{skey}.

func ParseRef added in v0.13.1

func ParseRef(s string) (Ref, error)

ParseRef parses a space URI (the lexicon's space-ref format).

func (Ref) HostAud added in v0.13.1

func (r Ref) HostAud() string

HostAud is the audience the authority answers to as space host.

func (Ref) RecordURI added in v0.13.1

func (r Ref) RecordURI(author, collection, rkey string) string

RecordURI is a record's URI within the space: at://{authority}/space/{spaceType}/{skey}/{author}/{collection}/{rkey}.

func (Ref) String added in v0.13.1

func (r Ref) String() string

type RepoCommit

type RepoCommit struct {
	SetHash *LtHash
}

RepoCommit tracks a space repo's contents as a set hash.

func NewRepoCommit

func NewRepoCommit() *RepoCommit

NewRepoCommit returns an empty repo.

func RepoCommitFromRecords

func RepoCommitFromRecords(records []RecordRef) *RepoCommit

RepoCommitFromRecords folds in a set of records.

func RepoCommitFromState added in v0.13.1

func RepoCommitFromState(state []byte) (*RepoCommit, error)

RepoCommitFromState resumes from a persisted set hash state (empty for nil).

func (*RepoCommit) Add

func (r *RepoCommit) Add(collection, rkey string, c cid.Cid) *RepoCommit

func (*RepoCommit) ApplyOp added in v0.13.1

func (r *RepoCommit) ApplyOp(op RepoOp) *RepoCommit

func (*RepoCommit) ApplyOps added in v0.13.1

func (r *RepoCommit) ApplyOps(ops []RepoOp) *RepoCommit

func (*RepoCommit) Matches

func (r *RepoCommit) Matches(c Commit) bool

Matches reports whether this repo's contents match a commit. Verify the commit first: on its own this says nothing about authenticity.

func (*RepoCommit) Remove

func (r *RepoCommit) Remove(collection, rkey string, c cid.Cid) *RepoCommit

func (*RepoCommit) ToCommit added in v0.14.1

func (r *RepoCommit) ToCommit(ctx CommitCtx) Commit

ToCommit builds a commit over the current contents.

type RepoOp added in v0.13.1

type RepoOp struct {
	Collection string
	Rkey       string
	Cid        *cid.Cid
	Prev       *cid.Cid
}

RepoOp is one oplog entry: a create has no Prev, a delete no Cid, an update both.

type RepoStarReader added in v0.14.1

type RepoStarReader struct {
	Commit Commit
	// contains filtered or unexported fields
}

RepoStarReader is an archive whose commit has verified against the response's MAC. Next streams its records, folding each into a running set hash, and only the io.EOF that ends a fully read archive shows the records match the commit, so drain it before trusting what it returned.

func VerifyRepoStar added in v0.14.1

func VerifyRepoStar(r io.Reader, params VerifyRepoParams) (*RepoStarReader, error)

VerifyRepoStar verifies an archive's commit and returns a reader for its records.

func (*RepoStarReader) Next added in v0.14.1

func (r *RepoStarReader) Next() (*VerifiedRecord, error)

Next returns the next record, or io.EOF once the archive has ended and its records match the commit.

type RepoVerificationError added in v0.13.1

type RepoVerificationError struct{ Message string }

RepoVerificationError is a space repo or record that does not verify.

func (*RepoVerificationError) Error added in v0.13.1

func (e *RepoVerificationError) Error() string

type SerializedRecord

type SerializedRecord struct {
	Collection string
	Rkey       string
	Cid        cid.Cid
	Bytes      []byte
}

SerializedRecord is a record's DRISL encoding.

func SerializeRecord

func SerializeRecord(collection, rkey string, record map[string]any) (SerializedRecord, error)

SerializeRecord encodes a record (atproto data model) as DRISL.

type SignatureError added in v0.13.1

type SignatureError struct {
	Message string
	Cause   error
}

SignatureError is a request signature failure (BadSpaceSignature).

func (*SignatureError) Error added in v0.13.1

func (e *SignatureError) Error() string

func (*SignatureError) Unwrap added in v0.13.1

func (e *SignatureError) Unwrap() error

type Signer

type Signer = atcrypto.PrivateKey

Signer signs space tokens.

type SigningKeyFunc added in v0.13.1

type SigningKeyFunc func(iss string, forceRefresh bool) (string, error)

SigningKeyFunc resolves the did:key an issuer signs with. forceRefresh asks for a fresh resolution, after a cached key failed to verify.

type StarParseError added in v0.14.1

type StarParseError struct{ Message string }

StarParseError is an archive that doesn't parse.

func (*StarParseError) Error added in v0.14.1

func (e *StarParseError) Error() string

type StarReader added in v0.14.1

type StarReader struct {
	Meta map[string]any
	// contains filtered or unexported fields
}

StarReader reads an archive's pairs lazily, after its header.

func ReadStar added in v0.14.1

func ReadStar(r io.Reader, maxValueLen uint64) (*StarReader, error)

ReadStar reads an archive's header. maxValueLen, if non-zero, lowers STAR's own value length limit. Lengths are checked before a frame is read, so an oversized frame is never buffered.

func (*StarReader) Next added in v0.14.1

func (sr *StarReader) Next() ([]byte, []byte, error)

Next returns the next pair, or io.EOF at the end of the archive. EOF is only allowed at a pair boundary.

type StarWriter added in v0.14.1

type StarWriter struct {
	// contains filtered or unexported fields
}

StarWriter writes an archive's pairs after its header.

func WriteStar added in v0.14.1

func WriteStar(w io.Writer, meta map[string]any) (*StarWriter, error)

WriteStar writes the magic bytes and the metadata, which must be a map with a string $type, as canonical DRISL.

func (*StarWriter) WritePair added in v0.14.1

func (sw *StarWriter) WritePair(key, value []byte) error

WritePair writes one key/value pair.

type Token added in v0.13.1

type Token struct {
	Header  TokenHeader
	Payload TokenPayload
}

func VerifySpaceToken

func VerifySpaceToken(typ TokenType, jwt string, opts VerifyTokenOpts) (*Token, error)

VerifySpaceToken parses a token, checks its timing, audience and subject, and verifies its signature against the issuer's key, retrying once with a freshly resolved key in case the key rotated.

type TokenCnf added in v0.13.1

type TokenCnf struct {
	Kid string `json:"kid"`
}

type TokenError added in v0.13.1

type TokenError struct {
	Code    string
	Message string
}

TokenError is a token failure with an XRPC-style code.

func (*TokenError) Error added in v0.13.1

func (e *TokenError) Error() string

type TokenHeader added in v0.13.1

type TokenHeader struct {
	Alg string `json:"alg"`
	Typ string `json:"typ"`
	Kid string `json:"kid,omitempty"`
}

type TokenPayload added in v0.13.1

type TokenPayload struct {
	Iss string    `json:"iss"`
	Sub string    `json:"sub"`
	Aud string    `json:"aud,omitempty"`
	Cnf *TokenCnf `json:"cnf,omitempty"`
	Iat int64     `json:"iat"`
	Exp int64     `json:"exp"`
	Jti string    `json:"jti"`
}

TokenPayload's field order matches the reference's JSON output. Sub is the space URI, or the client_id for a client attestation.

type TokenSpec added in v0.13.1

type TokenSpec struct {
	Typ          string
	Kid          string
	ExpiresInSec int64
	RequireAud   bool
	RequireCnf   bool
	SingleUse    bool
}

TokenSpec describes how a token type is minted and checked.

type TokenType

type TokenType string

TokenType is one of the three space JWT kinds.

const (
	TokenDelegation        TokenType = "delegation"
	TokenCredential        TokenType = "credential"
	TokenClientAttestation TokenType = "clientAttestation"
)

type VerifiedRecord

type VerifiedRecord struct {
	Collection string
	Rkey       string
	Cid        cid.Cid
	Record     map[string]any
}

type VerifiedRepo

type VerifiedRepo struct {
	Commit  Commit
	Records []VerifiedRecord
}

VerifiedRepo is a fully read and verified repo archive.

func VerifyRepoStarFull added in v0.14.1

func VerifyRepoStarFull(r io.Reader, params VerifyRepoParams) (*VerifiedRepo, error)

VerifyRepoStarFull verifies an archive and collects its records.

type VerifyCommitParams added in v0.14.1

type VerifyCommitParams struct {
	Space        string
	Repo         string
	SharedSecret []byte
	Mac          []byte
}

VerifyCommitParams names the space and repo that were requested, with the request's shared secret and the MAC from its response header.

type VerifyRecordParams added in v0.14.1

type VerifyRecordParams struct {
	URI          string
	SharedSecret []byte
	Mac          []byte
}

VerifyRecordParams names the record that was requested, with the request's shared secret and the MAC from its response header.

type VerifyRepoParams

type VerifyRepoParams = VerifyCommitParams

VerifyRepoParams is VerifyCommitParams for a repo archive.

type VerifyTokenOpts added in v0.13.1

type VerifyTokenOpts struct {
	GetSigningKey SigningKeyFunc
	// Aud and Sub, when set, must match the token's.
	Aud string
	Sub string
	// Now overrides the clock.
	Now time.Time
}

Jump to

Keyboard shortcuts

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