identity

package
v0.0.0-...-c10ddb5 Latest Latest
Warning

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

Go to latest
Published: Nov 14, 2025 License: Apache-2.0, MIT Imports: 20 Imported by: 90

Documentation

Overview

Package identity provides types and routines for resolving handles and DIDs from the network

The two main abstractions are a Directory interface for identity service implementations, and an Identity struct which represents core identity information relevant to atproto. The Directory interface can be nested, somewhat like HTTP middleware, to provide caching, observability, or other bespoke needs in more complex systems.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrNSIDResolutionFailed = fmt.Errorf("NSID resolution mechanism failed")
	ErrNSIDNotFound         = fmt.Errorf("NSID not associated with a DID")
)
View Source
var DefaultPLCURL = "https://plc.directory"
View Source
var ErrDIDNotFound = errors.New("DID not found")

Indicates that resolution process completed successfully, but the DID does not exist.

View Source
var ErrDIDResolutionFailed = errors.New("DID resolution failed")

Indicates that DID resolution process failed. A wrapped error may provide more context.

View Source
var ErrHandleMismatch = errors.New("handle/DID mismatch")

Indicates that resolution process completed successfully, handle mapped to a different DID. This is only returned when looking up a handle, not when looking up a DID.

View Source
var ErrHandleNotDeclared = errors.New("DID document did not declare a handle")

Indicates that DID document did not include any handle ("alsoKnownAs"). This is only returned when looking up a handle, not when looking up a DID.

View Source
var ErrHandleNotFound = errors.New("handle not found")

Indicates that resolution process completed successfully, but handle does not exist. This is only returned when looking up a handle, not when looking up a DID.

View Source
var ErrHandleReservedTLD = errors.New("handle top-level domain is disallowed")

Handle top-level domain (TLD) is one of the special "Reserved" suffixes, and not allowed for atproto use

View Source
var ErrHandleResolutionFailed = errors.New("handle resolution failed")

Indicates that handle resolution failed. A wrapped error may provide more context. This is only returned when looking up a handle, not when looking up a DID.

View Source
var ErrInvalidHandle = errors.New("invalid handle")

Handle was invalid, in a situation where a valid handle is required.

View Source
var ErrKeyNotDeclared = errors.New("DID document did not declare a relevant public key")

Indicates that DID document did not include a public key with the specified ID

Functions

This section is empty.

Types

type BaseDirectory

type BaseDirectory struct {
	// if non-empty, this string should have URL method, hostname, and optional port; it should not have a path or trailing slash
	PLCURL string
	// If not nil, this limiter will be used to rate-limit requests to the PLCURL
	PLCLimiter *rate.Limiter
	// If not nil, this function will be called inline with DID Web lookups, and can be used to limit the number of requests to a given hostname
	DIDWebLimitFunc func(ctx context.Context, hostname string) error
	// HTTP client used for did:web, did:plc, and HTTP (well-known) handle resolution
	HTTPClient http.Client
	// DNS resolver used for DNS handle resolution. Calling code can use a custom Dialer to query against a specific DNS server, or re-implement the interface for even more control over the resolution process
	Resolver net.Resolver
	// when doing DNS handle resolution, should this resolver attempt re-try against an authoritative nameserver if the first TXT lookup fails?
	TryAuthoritativeDNS bool
	// set of handle domain suffixes for for which DNS handle resolution will be skipped
	SkipDNSDomainSuffixes []string
	// set of fallback DNS servers (eg, domain registrars) to try as a fallback. each entry should be "ip:port", eg "8.8.8.8:53"
	FallbackDNSServers []string
	// skips bi-directional verification of handles when doing DID lookups (eg, `LookupDID`). Does not impact direct resolution (`ResolveHandle`) or handle-specific lookup (`LookupHandle`).
	//
	// The intended use-case for this flag is as an optimization for services which do not care about handles, but still want to use the `Directory` interface (instead of `ResolveDID`). For example, relay implementations, or services validating inter-service auth requests.
	SkipHandleVerification bool
	// User-Agent header for HTTP requests. Optional (ignored if empty string).
	UserAgent string
}

The zero value ('BaseDirectory{}') is a usable Directory.

func (*BaseDirectory) Lookup

func (*BaseDirectory) LookupDID

func (d *BaseDirectory) LookupDID(ctx context.Context, did syntax.DID) (*Identity, error)

func (*BaseDirectory) LookupHandle

func (d *BaseDirectory) LookupHandle(ctx context.Context, h syntax.Handle) (*Identity, error)

func (*BaseDirectory) Purge

func (d *BaseDirectory) Purge(ctx context.Context, atid syntax.AtIdentifier) error

func (*BaseDirectory) ResolveDID

func (d *BaseDirectory) ResolveDID(ctx context.Context, did syntax.DID) (*DIDDocument, error)

Resolves a DID to a parsed `DIDDocument` struct.

This method does not bi-directionally verify handles. Most atproto-specific code should use the `identity.Directory` interface ("Lookup" methods), which implement that check by default, and provide more ergonomic helpers for working with atproto-relevant information in DID documents.

Note that the `DIDDocument` might not include all the information in the original document. Use `ResolveDIDRaw()` to get the full original JSON.

func (*BaseDirectory) ResolveDIDRaw

func (d *BaseDirectory) ResolveDIDRaw(ctx context.Context, did syntax.DID) (json.RawMessage, error)

Low-level method for resolving a DID to a raw JSON document.

This method does not parse the DID document into an atproto-specific format, and does not bi-directionally verify handles. Most atproto-specific code should use the "Lookup*" methods, which do implement that functionality.

func (*BaseDirectory) ResolveHandle

func (d *BaseDirectory) ResolveHandle(ctx context.Context, handle syntax.Handle) (syntax.DID, error)

func (*BaseDirectory) ResolveHandleDNS

func (d *BaseDirectory) ResolveHandleDNS(ctx context.Context, handle syntax.Handle) (syntax.DID, error)

Does not cross-verify, only does the handle resolution step.

func (*BaseDirectory) ResolveHandleDNSAuthoritative

func (d *BaseDirectory) ResolveHandleDNSAuthoritative(ctx context.Context, handle syntax.Handle) (syntax.DID, error)

this is a variant of ResolveHandleDNS which first does an authoritative nameserver lookup, then queries there

func (*BaseDirectory) ResolveHandleDNSFallback

func (d *BaseDirectory) ResolveHandleDNSFallback(ctx context.Context, handle syntax.Handle) (syntax.DID, error)

variant of ResolveHandleDNS which uses any configured fallback DNS servers

func (*BaseDirectory) ResolveHandleWellKnown

func (d *BaseDirectory) ResolveHandleWellKnown(ctx context.Context, handle syntax.Handle) (syntax.DID, error)

func (*BaseDirectory) ResolveNSID

func (d *BaseDirectory) ResolveNSID(ctx context.Context, nsid syntax.NSID) (syntax.DID, error)

Resolves an NSID to a DID, as used for Lexicon resolution (using "_lexicon" DNS TXT record)

type CacheDirectory

type CacheDirectory struct {
	Inner            Directory
	ErrTTL           time.Duration
	InvalidHandleTTL time.Duration
	// contains filtered or unexported fields
}

CacheDirectory is an implementation of identity.Directory with local cache of Handle and DID

func NewCacheDirectory

func NewCacheDirectory(inner Directory, capacity int, hitTTL, errTTL, invalidHandleTTL time.Duration) CacheDirectory

Capacity of zero means unlimited size. Similarly, ttl of zero means unlimited duration.

func (*CacheDirectory) Lookup

func (*CacheDirectory) LookupDID

func (d *CacheDirectory) LookupDID(ctx context.Context, did syntax.DID) (*Identity, error)

func (*CacheDirectory) LookupDIDWithCacheState

func (d *CacheDirectory) LookupDIDWithCacheState(ctx context.Context, did syntax.DID) (*Identity, bool, error)

func (*CacheDirectory) LookupHandle

func (d *CacheDirectory) LookupHandle(ctx context.Context, h syntax.Handle) (*Identity, error)

func (*CacheDirectory) LookupHandleWithCacheState

func (d *CacheDirectory) LookupHandleWithCacheState(ctx context.Context, h syntax.Handle) (*Identity, bool, error)

func (*CacheDirectory) Purge

func (d *CacheDirectory) Purge(ctx context.Context, atid syntax.AtIdentifier) error

func (*CacheDirectory) ResolveHandle

func (d *CacheDirectory) ResolveHandle(ctx context.Context, h syntax.Handle) (syntax.DID, error)

type DIDDocument

type DIDDocument struct {
	DID                syntax.DID              `json:"id"`
	AlsoKnownAs        []string                `json:"alsoKnownAs,omitempty"`
	VerificationMethod []DocVerificationMethod `json:"verificationMethod,omitempty"`
	Service            []DocService            `json:"service,omitempty"`
}

type Directory

type Directory interface {
	LookupHandle(ctx context.Context, handle syntax.Handle) (*Identity, error)
	LookupDID(ctx context.Context, did syntax.DID) (*Identity, error)
	Lookup(ctx context.Context, atid syntax.AtIdentifier) (*Identity, error)

	// Flushes any cache of the indicated identifier. If directory is not using caching, can ignore this.
	Purge(ctx context.Context, atid syntax.AtIdentifier) error
}

Ergonomic interface for atproto identity lookup, by DID or handle.

The "Lookup" methods resolve identities (handle and DID), and return results in a compact, opinionated struct (`Identity`). They do bi-directional handle/DID verification by default. Clients and services should use these methods by default, instead of resolving handles or DIDs separately.

Looking up a handle which fails to resolve, or don't match DID alsoKnownAs, returns an error. When looking up a DID, if the handle does not resolve back to the DID, the lookup succeeds and returns an `Identity` where the Handle is the special `handle.invalid` value.

Some example implementations of this interface could be:

  • basic direct resolution on every call
  • local in-memory caching layer to reduce network hits
  • API client, which just makes requests to PDS (or other remote service)
  • client for shared network cache (eg, Redis)

func DefaultDirectory

func DefaultDirectory() Directory

Returns a reasonable Directory implementation for applications

type DocService

type DocService struct {
	ID              string `json:"id"`
	Type            string `json:"type"`
	ServiceEndpoint string `json:"serviceEndpoint"`
}

type DocVerificationMethod

type DocVerificationMethod struct {
	ID                 string `json:"id"`
	Type               string `json:"type"`
	Controller         string `json:"controller"`
	PublicKeyMultibase string `json:"publicKeyMultibase"`
}

type Identity

type Identity struct {
	DID syntax.DID

	// Handle/DID mapping must be bi-directionally verified. If that fails, the Handle should be the special 'handle.invalid' value
	Handle syntax.Handle

	// These fields represent a parsed subset of a DID document. They are all nullable. Note that the services and keys maps do not preserve order, so they don't exactly round-trip DID documents.
	AlsoKnownAs []string
	Services    map[string]ServiceEndpoint
	Keys        map[string]VerificationMethod
}

Represents an atproto identity. Could be a regular user account, or a service account (eg, feed generator)

func ParseIdentity

func ParseIdentity(doc *DIDDocument) Identity

Extracts the information relevant to atproto from an arbitrary DID document.

Always returns an invalid Handle field; calling code should only populate that field if it has been bi-directionally verified.

func (*Identity) DIDDocument

func (ident *Identity) DIDDocument() DIDDocument

Helper to generate a DID document based on an identity. Note that there is flexibility around parsing, and this won't necessarily "round-trip" for every valid DID document.

func (*Identity) DeclaredHandle

func (i *Identity) DeclaredHandle() (syntax.Handle, error)

Returns an atproto handle from the alsoKnownAs URI list for this identifier. Returns an error if there is no handle, or if an at:// URI fails to parse as a handle.

Note that this handle is *not* necessarily to be trusted, as it may not have been bi-directionally verified. The 'Handle' field on the 'Identity' should contain either a verified handle, or the special 'handle.invalid' indicator value.

func (*Identity) GetPublicKey

func (i *Identity) GetPublicKey(id string) (atcrypto.PublicKey, error)

Identifies and parses a specified service signing public key out of any keys in this identity's DID document.

Returns [ErrKeyNotFound] if there is no such key.

Note that atcrypto.PublicKey is an interface, not a concrete type.

func (*Identity) GetServiceEndpoint

func (i *Identity) GetServiceEndpoint(id string) string

Returns the service endpoint URL for specified service ID (the fragment part of identifier, not including the hash symbol).

The endpoint should be an HTTP URL with method, hostname, and optional port. It may or may not include path segments.

Returns an empty string if the service isn't found, or if the URL fails to parse.

func (*Identity) PDSEndpoint

func (i *Identity) PDSEndpoint() string

The home PDS endpoint for this identity, if one is included in the DID document.

The endpoint should be an HTTP URL with method, hostname, and optional port. It may or may not include path segments.

Returns an empty string if the service isn't found, or if the URL fails to parse.

func (*Identity) PublicKey

func (i *Identity) PublicKey() (atcrypto.PublicKey, error)

Identifies and parses the atproto repo signing public key, specifically, out of any keys in this identity's DID document.

Returns [ErrKeyNotFound] if there is no such key.

Note that atcrypto.PublicKey is an interface, not a concrete type.

type MockDirectory

type MockDirectory struct {
	Handles    map[syntax.Handle]syntax.DID
	Identities map[syntax.DID]Identity
	// contains filtered or unexported fields
}

A fake identity directory, for use in tests

func NewMockDirectory

func NewMockDirectory() MockDirectory

func (*MockDirectory) Insert

func (d *MockDirectory) Insert(ident Identity)

func (*MockDirectory) Lookup

func (*MockDirectory) LookupDID

func (d *MockDirectory) LookupDID(ctx context.Context, did syntax.DID) (*Identity, error)

func (*MockDirectory) LookupHandle

func (d *MockDirectory) LookupHandle(ctx context.Context, h syntax.Handle) (*Identity, error)

func (*MockDirectory) Purge

func (*MockDirectory) ResolveDID

func (d *MockDirectory) ResolveDID(ctx context.Context, did syntax.DID) (*DIDDocument, error)

func (*MockDirectory) ResolveDIDRaw

func (d *MockDirectory) ResolveDIDRaw(ctx context.Context, did syntax.DID) (json.RawMessage, error)

func (*MockDirectory) ResolveHandle

func (d *MockDirectory) ResolveHandle(ctx context.Context, h syntax.Handle) (syntax.DID, error)

type Resolver

type Resolver interface {
	ResolveDID(ctx context.Context, did syntax.DID) (*DIDDocument, error)
	ResolveDIDRaw(ctx context.Context, did syntax.DID) (json.RawMessage, error)
	ResolveHandle(ctx context.Context, handle syntax.Handle) (syntax.DID, error)
}

Low-level interface for resolving DIDs and atproto handles.

Most atproto code should use the `identity.Directory` interface instead.

type ServiceEndpoint

type ServiceEndpoint struct {
	Type string
	URL  string
}

Sub-field type for Identity, representing a service endpoint URL declared in the DID document.

type VerificationMethod

type VerificationMethod struct {
	Type               string
	PublicKeyMultibase string
}

Sub-field type for Identity, representing a cryptographic public key declared as a "verificationMethod" in the DID document.

Directories

Path Synopsis
Identity Directory implementation which makes HTTP requests to a dedicated identity service.
Identity Directory implementation which makes HTTP requests to a dedicated identity service.
cmd
atp-id command
Identity Directory implementation with tiered caching, using Redis.
Identity Directory implementation with tiered caching, using Redis.

Jump to

Keyboard shortcuts

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