invite

package
v0.2.2 Latest Latest
Warning

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

Go to latest
Published: Jul 27, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package invite implements the Contract-B agent-network INVITE token: a self-contained, UNSIGNED transport an admin emits so a future `identity enroll` can bootstrap trust in a network's root anchor.

Why unsigned is correct

An invite CARRIES the network's PUBLIC trust anchor (the ed25519 ROOT public key) itself, plus where to reach the network (the relay) and the network id derived from that key. A signature would have to be verified under some key — but the key in question IS the anchor we are trying to establish trust in, so a self-signature proves nothing. Authenticity instead comes OUT OF BAND: the admin reads the Fingerprint (the vmnet1:… network id) to the member over a TRUSTED channel, and `enroll` asks the member to confirm it before trusting the anchor. The token is a convenience transport, not a trust root.

The token + URL format

The bootstrap token is the invitePrefix ("vmenroll1:") followed by the base64url-NOPADDING encoding of JSON{ network_id, relay, root_pubkey }. The enroll URL is relay + "/enroll#" + the token (the token rides the URL fragment, after the '#').

The wire body uses snake_case keys (the FieldNetworkID / FieldRelay / FieldRootPubKey SSOT constants); root_pubkey is base64-std (padded) of the 32-byte ed25519 root key. The token uses base64url-NOPADDING so it is URL/fragment-safe.

The integrity binding (load-bearing)

network_id MUST equal registry.NetworkID(root_pubkey). Decode RE-DERIVES the id from the carried key and rejects any mismatch (ErrNetworkIDMismatch), so a relay cannot advertise a victim network's id while substituting its OWN anchor key. Every field is also individually gated: the root key is base64-std, 32-byte, and small-order-rejected via registry.NewPublicKey; the relay is non-empty; the JSON is strict (unknown fields and trailing data rejected).

Decode accepts either a bare token or the full enroll URL (it reads the token from the fragment after the first '#'). Encode validates with the SAME rules it would accept on Decode, so a token Encode produces always round-trips.

Reuse

This package reimplements no crypto: it builds on internal/identity/registry for NetworkID (the keypair-bound id derivation) and NewPublicKey (the wrong-length / small-order validated key constructor).

Index

Constants

View Source
const (
	FieldNetworkID  = "network_id"
	FieldRelay      = "relay"
	FieldRootPubKey = "root_pubkey"
)

Wire field names (SSOT). These are the EXACT snake_case keys of the invite's transported JSON body. They are referenced from the encode/decode paths and asserted by tests, never inlined.

View Source
const (
	// ErrBadPrefix is returned when the token (after URL-fragment extraction) does
	// not start with invitePrefix.
	ErrBadPrefix = "invite: token must start with " + invitePrefix
	// ErrBadBase64 is returned when the token body is not valid base64url-nopad.
	ErrBadBase64 = "invite: token body is not base64url (no padding)"
	// ErrBadJSON is returned when the decoded body is not a strict JSON object
	// (bad JSON, unknown field, or trailing data — fail closed).
	ErrBadJSON = "invite: token body is not a valid invite JSON object"
	// ErrEmptyRelay is returned when relay is empty.
	ErrEmptyRelay = "invite: relay must not be empty"
	// ErrBadRootPubKey is returned when root_pubkey is not base64-std of a valid
	// 32-byte ed25519 public key (bad base64 / wrong length / small-order).
	ErrBadRootPubKey = "invite: root_pubkey must be base64-std of an ed25519 public key"
	// ErrNetworkIDMismatch is returned when network_id != NetworkID(root_pubkey).
	// This is the integrity check that binds the advertised id to the actual
	// anchor key, so a tampered or substituted network_id is rejected.
	ErrNetworkIDMismatch = "invite: network_id does not match NetworkID(root_pubkey)"
)

Reject messages (SSOT — referenced from Decode/Encode and asserted by tests/callers, never inlined). Each failure mode is a distinct typed error so callers can tell a malformed token from a tampered one.

Variables

This section is empty.

Functions

func Encode

func Encode(inv Invite) (token string, url string, err error)

Encode validates inv (the SAME rules as Decode, so a token Encode produces always round-trips) and returns the opaque token plus the enroll URL that carries the token in its fragment. It FAILS CLOSED: any validation failure returns a non-nil error and empty strings.

func Fingerprint

func Fingerprint(inv Invite) string

Fingerprint returns the human-comparable out-of-band fingerprint, which IS the network_id (the vmnet1:… string). This is the value the admin reads to the member over a TRUSTED channel, and the value `identity enroll` will ask the member to confirm before it trusts the anchor.

Types

type Invite

type Invite struct {
	// NetworkID is "vmnet1:" + hex(SHA-256(root pubkey)[:16]) — registry.NetworkID
	// of RootPubKey. Decode re-derives and re-checks this binding.
	NetworkID string
	// Relay is the relay base URL, e.g. "https://chat.acme.com".
	Relay string
	// RootPubKey is base64-std (padded) of the 32-byte ed25519 ROOT public key.
	RootPubKey string
}

Invite is a self-contained, UNSIGNED transport of a network's PUBLIC trust anchor plus where to reach it. It is deliberately unsigned: it CARRIES the root pubkey (the trust anchor itself), so authenticity comes from the out-of-band fingerprint comparison (Fingerprint), not from a signature.

func Decode

func Decode(s string) (Invite, error)

Decode accepts EITHER a bare token (vmenroll1:…) OR an enroll URL whose fragment is the token (https://…/enroll#vmenroll1:…). It strips the prefix, base64url-nopad decodes, strictly JSON-unmarshals (unknown fields + trailing data rejected), and VALIDATES — including the critical network_id ⇔ root_pubkey integrity check. It FAILS CLOSED with a typed error on any failure.

Jump to

Keyboard shortcuts

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