Documentation
¶
Overview ¶
Package auth implements the cryptographic primitives used by the asobi-cli device-code login flow.
Protocol: clean-room reimplementation of the Supabase CLI device login using standard primitives (NIST SP 800-56A for ECDH key agreement on P-256, RFC 5869 HKDF-SHA256 for key derivation, NIST SP 800-38D AES-256-GCM for authenticated encryption).
The CLI generates an ephemeral P-256 keypair and sends its public key to asobi_saas. The saas side generates its own ephemeral keypair, derives the ECDH shared secret, encrypts the issued API key with AES-256-GCM, and returns the ciphertext plus its public key. The CLI recomputes the shared secret from its private key and the saas public key, then decrypts.
The Erlang counterpart lives in asobi_saas_cli_crypto. Both sides use the same HKDF info string ("asobi-cli-device-login-v1") and an empty salt to maintain domain-separation across future versions.
Index ¶
- Constants
- func CreateEnv(creds *Credentials, game, name, size string) (map[string]interface{}, error)
- func Decrypt(ciphertext, tag, nonce, key []byte) ([]byte, error)
- func DeleteCredentials() error
- func DeleteEnv(creds *Credentials, game, name string) error
- func DeployBundle(creds *Credentials, game, name string, bundle []byte) (map[string]interface{}, error)
- func DeriveSharedSecret(peerPublic, ownPrivate []byte) ([]byte, error)
- func Destroy(creds *Credentials, envID string) error
- func DeviceFingerprint() string
- func Encrypt(plaintext, key []byte) (nonce, ciphertext, tag []byte, err error)
- func EnvAction(creds *Credentials, game, name, action string) error
- func ListEnvs2(creds *Credentials, game string) ([]map[string]interface{}, error)
- func RefreshAccessToken(creds *Credentials) (string, error)
- func ResizeEnv(creds *Credentials, game, name, size string) error
- func SaveCredentials(creds *Credentials) error
- type Credentials
- type Environment
- type Game
- type Keypair
Constants ¶
const HKDFInfo = "asobi-cli-device-login-v1"
HKDFInfo is the domain-separated info string used by HKDF-SHA256. Must match the ?INFO macro in asobi_saas_cli_crypto.erl exactly.
Variables ¶
This section is empty.
Functions ¶
func CreateEnv ¶
func CreateEnv(creds *Credentials, game, name, size string) (map[string]interface{}, error)
CreateEnv creates a named environment for a game with a given size.
func Decrypt ¶
Decrypt opens a ciphertext with AES-256-GCM using the given key, nonce, and tag. Returns an error if the tag does not verify (tamper detection) or if the key length or nonce length is wrong.
func DeleteCredentials ¶
func DeleteCredentials() error
DeleteCredentials removes the stored credentials file.
func DeleteEnv ¶
func DeleteEnv(creds *Credentials, game, name string) error
DeleteEnv deletes a named environment within a game.
func DeployBundle ¶
func DeployBundle(creds *Credentials, game, name string, bundle []byte) (map[string]interface{}, error)
DeployBundle uploads a zip bundle to a named environment within a game.
func DeriveSharedSecret ¶
DeriveSharedSecret runs ECDH between a peer public key and our own private key, then stretches the raw shared secret into a 32-byte AES-256 key via HKDF-SHA256 with a fixed empty salt and the asobi-specific info string.
func Destroy ¶
func Destroy(creds *Credentials, envID string) error
Destroy deletes an environment by ID. Idempotent.
func DeviceFingerprint ¶
func DeviceFingerprint() string
DeviceFingerprint returns a stable identifier for this machine.
func Encrypt ¶
Encrypt seals a plaintext with AES-256-GCM. The caller supplies a 32-byte key (typically the output of DeriveSharedSecret). A fresh 12-byte random nonce is generated per call. Returns (nonce, ciphertext, tag) as three separate byte slices so the wire format matches the Erlang side's encrypt/2 return shape.
func EnvAction ¶
func EnvAction(creds *Credentials, game, name, action string) error
EnvAction performs stop/start on a named environment within a game.
func ListEnvs2 ¶
func ListEnvs2(creds *Credentials, game string) ([]map[string]interface{}, error)
ListEnvs2 returns environments for a game using the new flat model.
func RefreshAccessToken ¶
func RefreshAccessToken(creds *Credentials) (string, error)
RefreshAccessToken exchanges a refresh token for a new access token. The device secret issued at login must match the hash stored server-side.
func ResizeEnv ¶
func ResizeEnv(creds *Credentials, game, name, size string) error
ResizeEnv changes the size of a named environment within a game.
func SaveCredentials ¶
func SaveCredentials(creds *Credentials) error
SaveCredentials writes credentials to disk with 0600 permissions.
Types ¶
type Credentials ¶
type Credentials struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
SaasURL string `json:"saas_url"`
EngineURL string `json:"engine_url"`
TenantID string `json:"tenant_id"`
GameID string `json:"game_id"`
EnvironmentID string `json:"environment_id"`
EnvName string `json:"env_name"`
DeviceFingerprint string `json:"device_fingerprint"`
// DeviceSecret is the server-issued secret that binds the refresh token
// to this CLI install. Presented on refresh; never sent anywhere else.
DeviceSecret string `json:"device_secret"`
// ActiveGame is the slug of the game selected via `asobi use <slug>`.
// The effective game for env operations resolves from --game, then this.
ActiveGame string `json:"active_game"`
}
Credentials holds the CLI's saas session tokens and associated context. Stored in ~/.asobi/credentials.json with 0600 permissions. On Windows, where the file mode does not create an ACL, the blob is additionally encrypted at rest with DPAPI scoped to the current user (see protect).
func LoadCredentials ¶
func LoadCredentials() (*Credentials, error)
LoadCredentials reads the stored CLI credentials. Returns nil if no credentials exist (not an error — user hasn't logged in yet). The ASOBI_ACCESS_TOKEN env var overrides the stored access token.
func Login ¶
func Login(saasURL, tokenName string) (*Credentials, error)
Login runs the full ECDH-encrypted device-code login flow. It returns credentials on success or an error.
type Environment ¶
type Environment struct {
ID string `json:"id"`
Name string `json:"name"`
Status string `json:"status"`
IsEphemeral bool `json:"is_ephemeral"`
ExpiresAt string `json:"expires_at"`
InsertedAt string `json:"inserted_at"`
}
Environment is a single env returned by ListEnvs.
func ListEnvs ¶
func ListEnvs(creds *Credentials, ephemeralOnly bool) ([]Environment, error)
ListEnvs returns all environments for the current game. If ephemeralOnly is true, only ephemeral envs are returned.
type Game ¶
Game is a single game returned by ListGames.
func ListGames ¶
func ListGames(creds *Credentials) ([]Game, error)
ListGames returns the games belonging to the caller's tenant.
type Keypair ¶
Keypair holds a raw P-256 public key (65 bytes, uncompressed SEC1) and its corresponding private scalar (32 bytes).
func GenerateKeypair ¶
GenerateKeypair produces a fresh ephemeral P-256 keypair.