protocol

package
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Sep 28, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Overview

Package protocol is the SyncMyEnv sync protocol: wire types shared by the CLI and the server, plus a small HTTP client.

It is public on purpose. The server imports it (so client and server can't drift), and anyone auditing the zero-knowledge claim can read exactly what crosses the wire: account metadata, ciphertext, and opaque IDs. Never a plaintext value, path, or key.

Model (works over any blob store; the server adds auth + push):

keyring            the wrapped vault key (keys.json) — safe to store remotely
objects/<id>       sealed revisions; id = hex(sha256(ciphertext)), immutable
log                append-only entries, each written by exactly one device
                   (device_id, device_seq) → object + sealed metadata payload

Devices only ever append to their own sequence, so there are no write conflicts on the server; merging happens on the clients.

Index

Constants

View Source
const (
	MaxObjectSize  = 2 << 20 // bytes of ciphertext
	MaxPayloadSize = 16 << 10
	MaxLogBatch    = 200
)

Limits enforced by the server (and respected by the client).

View Source
const Version = 1

Version of the protocol spoken by this package.

Variables

This section is empty.

Functions

func IsStatus

func IsStatus(err error, status int) bool

IsStatus reports whether err is an APIError with the given HTTP status.

func ObjectID

func ObjectID(ciphertext []byte) string

ObjectID is the content address of a ciphertext blob. Hashing ciphertext reveals nothing: age output is randomized.

func SigningMessage

func SigningMessage(vaultID string, deviceSeq int64, objectID string, payload []byte) []byte

SigningMessage binds a signature to the vault, the device's sequence number, the object and the exact sealed payload (no cut-and-paste between vaults, positions or objects).

func ValidateURL

func ValidateURL(raw string) (string, error)

ValidateURL normalizes a server address and requires https, except for localhost / private LAN development. Without a scheme, a public domain ("env.example.com") means https://; a local or private address ("localhost:8080", "192.168.1.20:8080") means http://.

Types

type APIError

type APIError struct {
	Status  int
	Code    string
	Message string
}

APIError is a non-2xx response.

func (*APIError) Error

func (e *APIError) Error() string

type AppendLogRequest

type AppendLogRequest struct {
	Entries []LogEntry `json:"entries"`
}

type AppendLogResponse

type AppendLogResponse struct {
	Accepted int `json:"accepted"`
}

type Client

type Client struct {
	BaseURL string
	Token   string
	HTTP    *http.Client
	UA      string
}

Client talks to a SyncMyEnv server. Token is a device token (sme_dev_…).

func (*Client) AppendLog

func (c *Client) AppendLog(ctx context.Context, vault string, entries []LogEntry) error

func (*Client) CreateVault

func (c *Client) CreateVault(ctx context.Context, name string) (*Vault, error)

func (*Client) DeviceCode

func (c *Client) DeviceCode(ctx context.Context, name string) (*DeviceCodeResponse, error)

func (*Client) DeviceToken

func (c *Client) DeviceToken(ctx context.Context, deviceCode string) (*DeviceTokenResponse, error)

DeviceToken polls once. While waiting it returns an *APIError whose Code is authorization_pending or slow_down.

func (*Client) GetKeyring

func (c *Client) GetKeyring(ctx context.Context, vault string) ([]byte, error)

func (*Client) GetObject

func (c *Client) GetObject(ctx context.Context, vault, id string) ([]byte, error)

GetObject downloads a blob and verifies it matches its content address.

func (*Client) Me

func (c *Client) Me(ctx context.Context) (*Me, error)

func (*Client) PutKeyring

func (c *Client) PutKeyring(ctx context.Context, vault string, keyfile []byte) error

func (*Client) PutObject

func (c *Client) PutObject(ctx context.Context, vault string, data []byte) (string, error)

func (*Client) ReadLog

func (c *Client) ReadLog(ctx context.Context, vault string, since int64) (*ReadLogResponse, error)

func (*Client) RevokeSelf

func (c *Client) RevokeSelf(ctx context.Context) error

RevokeSelf revokes this device's own token (logout).

func (*Client) Vaults

func (c *Client) Vaults(ctx context.Context) ([]Vault, error)

type CreateVaultRequest

type CreateVaultRequest struct {
	Name string `json:"name"`
}

type Device

type Device struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

type DeviceCodeRequest

type DeviceCodeRequest struct {
	DeviceName string `json:"device_name"`
	PublicKey  string `json:"public_key,omitempty"`
}

type DeviceCodeResponse

type DeviceCodeResponse struct {
	DeviceCode              string `json:"device_code"`
	UserCode                string `json:"user_code"`
	VerificationURI         string `json:"verification_uri"`
	VerificationURIComplete string `json:"verification_uri_complete"`
	ExpiresIn               int    `json:"expires_in"`
	Interval                int    `json:"interval"`
}

type DeviceTokenRequest

type DeviceTokenRequest struct {
	DeviceCode string `json:"device_code"`
}

type DeviceTokenResponse

type DeviceTokenResponse struct {
	AccessToken string `json:"access_token"`
	TokenType   string `json:"token_type"`
	Device      Device `json:"device"`
}

type EntryPayload

type EntryPayload struct {
	V         int    `json:"v"`
	Path      string `json:"path"`    // "~/Projects/app/.env" (home-relative when possible)
	Project   string `json:"project"` // display name
	Size      int    `json:"size"`
	Source    string `json:"source"` // protect | snapshot | restore
	Device    string `json:"device"` // human device name
	CreatedAt int64  `json:"created_at"`
}

EntryPayload is the plaintext inside LogEntry.Payload. The server never sees it.

type ErrorBody

type ErrorBody struct {
	Error struct {
		Code    string `json:"code"`
		Message string `json:"message"`
	} `json:"error"`
}

ErrorBody is the server's error shape: {"error": {"code", "message"}}.

type LogEntry

type LogEntry struct {
	DeviceSeq  int64  `json:"device_seq"`
	ObjectID   string `json:"object_id"`
	Payload    []byte `json:"payload"`     // sealed EntryPayload (base64 in JSON)
	DeviceKey  []byte `json:"device_key"`  // ed25519 public key
	DeviceCert []byte `json:"device_cert"` // HMAC(vault cert key, DeviceKey)
	Signature  []byte `json:"signature"`   // ed25519 over SigningMessage
}

LogEntry is written by one device. Payload is sealed to the vault recipient; the server stores it opaquely.

Authenticity: sealing to the vault recipient is something anyone who knows the public key can do — including the server. So each entry is signed by the device's Ed25519 key (Signature over SigningMessage), and the device key carries a certificate (DeviceCert) that only vault-key holders can create or verify. Clients drop entries that fail either check.

type Me

type Me struct {
	User   User    `json:"user"`
	Device *Device `json:"device"`
}

type ReadLogResponse

type ReadLogResponse struct {
	Entries []StoredEntry `json:"entries"`
	Next    int64         `json:"next"` // pass as ?since= to continue
	More    bool          `json:"more"`
}

type StoredEntry

type StoredEntry struct {
	ID         int64  `json:"id"` // global cursor, strictly increasing per vault
	DeviceID   string `json:"device_id"`
	DeviceSeq  int64  `json:"device_seq"`
	ObjectID   string `json:"object_id"`
	Payload    []byte `json:"payload"`
	DeviceKey  []byte `json:"device_key"`
	DeviceCert []byte `json:"device_cert"`
	Signature  []byte `json:"signature"`
	CreatedAt  int64  `json:"created_at"`
}

StoredEntry is a log entry as read back, with server-assigned ordering.

type User

type User struct {
	ID    string `json:"id"`
	Email string `json:"email"`
}

type Vault

type Vault struct {
	ID string `json:"id"`
	// Fingerprint of the vault's public recipient, once a keyring is uploaded.
	// Lets a new device find "its" vault without decrypting anything.
	Fingerprint string `json:"fingerprint,omitempty"`
	CreatedAt   int64  `json:"created_at"`
}

type VaultList

type VaultList struct {
	Vaults []Vault `json:"vaults"`
}

Jump to

Keyboard shortcuts

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