Documentation
¶
Index ¶
- Constants
- func ApplyCredentialEntry(active storepaths.ActivePaths, entry CredentialEntry, kr *crypto.Keyring, ...) error
- func ArchiveAuthenticated(err error) bool
- func BuildManagedArchivePath(paths storepaths.Paths, ts string) string
- func CreateTarGzArchive(srcDir, destPath string) (err error)
- func DeleteManagedBackup(paths storepaths.Paths, archivePath string) error
- func ExportAllKeys(paths storepaths.Paths, srcDir, destDir string, kr *crypto.Keyring, ...) (map[string]string, error)
- func ExportKey(paths storepaths.Paths, srcDir, destDir, address string, kr *crypto.Keyring, ...) (string, int64, error)
- func ExtractTarGzArchive(archivePath, destDir string) error
- func FileSHA256(path string) (string, int64, error)
- func FormatFileSize(bytes int64) string
- func IsArchivePath(path string) bool
- func ParseBackup(decryptedJSON []byte) ([]byte, error)
- func PrepareRestoreSource(source string) (string, func(), error)
- func ResolveBackupKeysDir(source string) string
- func ResolveManagedBackupPath(paths storepaths.Paths, archivePath string) (string, error)
- func RestoreKeyMetadata(keyJSON []byte) (keyType, selector string, hasLogicSigBytecode bool, err error)
- func ScanBackupFiles(dir string) ([]string, error)
- func StatManagedBackupArchive(archivePath string) (os.FileInfo, error)
- func WriteReadme(destDir string) error
- func WriteSealedManifest(destDir string, role noderole.Role, createdAt time.Time, ...) error
- type ArchiveResult
- type AuthenticatedArchiveError
- type CreateKeysArchiveRequest
- type CredentialEntry
- type ManagedBackupInfo
- type Manifest
- type ManifestMember
- type RestoreClassification
- type RestoreConflict
- type RestoreError
- type RestoreKeyInfo
- type RestoreLogger
- type RestorePreview
- type RestoreSet
- type Restorer
- func (r Restorer) RestoreActiveForRebuild(keysDir, selector string, kr *crypto.Keyring, exportPassphrase []byte) (string, error)
- func (r Restorer) RestoreKey(keysDir, selector string, kr *crypto.Keyring, exportPassphrase []byte) (string, error)
- func (r Restorer) WithActiveNamespace(active storepaths.ActivePaths) Restorer
- func (r Restorer) WithLogger(logf RestoreLogger) Restorer
- func (r Restorer) WithNodeRole(role noderole.Role) Restorer
- func (r Restorer) WithOverwrite(overwrite bool) Restorer
- type VerifyReport
- type VerifyResult
Constants ¶
const ( // ManifestFileName is the sealed archive manifest: the single record // covering every other archive member, encrypted under the export // passphrase with the standalone envelope the .apb payloads use. ManifestFileName = "manifest.sealed" // ManifestSchema identifies the sealed manifest inside its envelope. // The schema lives in the sealed plaintext, so it also separates this // record from any other standalone-encrypted payload. ManifestSchema = "aplane.credential-backup.manifest.v1" ManifestSchemaVersion = 1 )
const ReadmeContent = `# Signer Key Backup
This backup contains encrypted private keys backed up from Signer.
Backups are normally packaged as a .tar.gz archive containing this README, an
apb/ directory, and manifest.sealed. If you extracted the archive first, the
same instructions apply to the extracted directory.
manifest.sealed describes the archive: it lists every other member with its
SHA-256 digest and records the source node role. It is encrypted with the same
envelope as the .apb payloads, under the same export passphrase, so APlane can
detect a member that was removed, added, or altered after the backup was made.
## File Format
Each ` + "`.apb`" + ` file is named after the Algorand address it controls (e.g., ` + "`ABC123...XYZ.apb`" + `).
Each file is self-contained: it can be decrypted with only the file and the export passphrase (no additional metadata files are needed).
Each encrypted payload is the complete canonical managed credential record,
including durable signing metadata. Backups do not contain policy, approval
settings, network mappings, templates, endpoints, tokens, or operator config.
## Encryption Format (envelope_version 2)
Each ` + "`.apb`" + ` file is a JSON document with four fields:
- ` + "`envelope_version`" + `: Always 2 (self-contained standalone format)
- ` + "`salt`" + `: Base64-encoded 32-byte random salt for Argon2id key derivation
- ` + "`nonce`" + `: Base64-encoded 12-byte random nonce for AES-256-GCM
- ` + "`ciphertext`" + `: Base64-encoded AES-256-GCM encrypted data
### Key Derivation
The encryption key is derived using Argon2id with the following parameters:
- Time (iterations): 2
- Memory: 64 MB (65536 KiB)
- Threads: 4
- Output key length: 32 bytes (AES-256)
### Decryption Steps
1. Parse the JSON file
2. Base64-decode the ` + "`salt`" + `, ` + "`nonce`" + `, and ` + "`ciphertext`" + ` fields
3. Derive the AES-256 key: ` + "`Argon2id(passphrase, salt, time=2, memory=64MB, threads=4, keyLen=32)`" + `
4. Decrypt using AES-256-GCM with the derived key and nonce
5. The decrypted plaintext is one canonical managed credential JSON object.
## Restoring Keys
### Using apadmin (Recommended)
Import, preview, and apply through the running signer daemon:
` + "```bash" + `
apadmin backup import /path/to/this/backup.tar.gz
apadmin restore preview this-backup.tar.gz
apadmin restore apply this-backup.tar.gz
` + "```" + `
Or apply only one address:
` + "```bash" + `
apadmin restore apply this-backup.tar.gz --address <ADDRESS>
` + "```" + `
You will be prompted for the export passphrase used to encrypt the backup.
The running signer daemon encrypts restored keys into the target keystore. For
normal restore, credentials immediately operate under the destination's
current policy and configuration. Use ` + "`--replace-existing`" + ` only when
you explicitly intend to replace conflicting destination credentials. For
replacement-keystore rescue when no identity directory exists, use
` + "`apstore rebuild /path/to/this/backup.tar.gz`" + `, adding
` + "`--role sentry`" + ` when rebuilding a sentry store; rebuild verifies
that the requested destination role matches the archive's authenticated source role.
### Manual Decryption
If you need to decrypt manually, use the Argon2id and AES-256-GCM parameters documented above. Most languages have libraries for both (e.g., Python ` + "`argon2-cffi`" + ` + ` + "`cryptography`" + `, Go ` + "`golang.org/x/crypto/argon2`" + ` + ` + "`crypto/aes`" + `).
## Security Notes
- **Keep this backup secure**: These files contain your private keys
- **Remember your export passphrase**: Without it, the keys cannot be decrypted
- **Store offline**: Consider keeping backups on offline media (USB drives, etc.)
- **Multiple copies**: Keep backups in multiple secure locations
---
*Backup created by APlane*
`
ReadmeContent is the README.md content written to backup directories
Variables ¶
This section is empty.
Functions ¶
func ApplyCredentialEntry ¶ added in v0.35.0
func ApplyCredentialEntry( active storepaths.ActivePaths, entry CredentialEntry, kr *crypto.Keyring, replaceExisting bool, ) error
ApplyCredentialEntry writes only managed credential authority and derived public witness metadata. It never installs templates or changes key-type generation state.
func ArchiveAuthenticated ¶ added in v0.35.0
ArchiveAuthenticated reports whether err proves that the archive manifest and export passphrase authenticated before a later validation failure.
func BuildManagedArchivePath ¶
func BuildManagedArchivePath(paths storepaths.Paths, ts string) string
func CreateTarGzArchive ¶
CreateTarGzArchive packages the contents of srcDir into a tar.gz archive.
func DeleteManagedBackup ¶
func DeleteManagedBackup(paths storepaths.Paths, archivePath string) error
func ExportAllKeys ¶
func ExportAllKeys(paths storepaths.Paths, srcDir, destDir string, kr *crypto.Keyring, exportPassphrase []byte) (map[string]string, error)
ExportAllKeys exports all managed credential files from the keystore to a standalone backup directory. Each file is decrypted with the store's keyring and re-encrypted with the export passphrase using standalone encryption (envelope_version 2). No .keystore file is written — each backup file is self-contained.
The backup is complete or fails: silently omitting damaged authority would make the sealed archive inventory look complete when it is not.
func ExportKey ¶
func ExportKey(paths storepaths.Paths, srcDir, destDir, address string, kr *crypto.Keyring, exportPassphrase []byte) (string, int64, error)
ExportKey exports a single key file from the keystore to a standalone backup. It decrypts the key with the store's keyring, then re-encrypts it with the export passphrase using standalone encryption (envelope_version 2). Returns the SHA256 checksum of the written key file and its size.
func ExtractTarGzArchive ¶
ExtractTarGzArchive extracts a tar.gz archive into destDir. Extracted directories and files are owner-private regardless of archive mode bits so validation residue is safe inside a private signer store. The archive is opened with the same no-follow regular-file enforcement as the recover path, and extraction is bounded (entry count and total decompressed size) so a crafted archive cannot exhaust the disk.
func FileSHA256 ¶
FileSHA256 returns the SHA-256 checksum and size of path.
func FormatFileSize ¶
FormatFileSize formats a file size in human-readable format
func IsArchivePath ¶
IsArchivePath reports whether the path looks like a supported backup archive.
func ParseBackup ¶
ParseBackup returns the complete canonical managed credential payload. Backup bundles were an internal pre-release format and are deliberately not accepted by the first supported credential-backup contract.
func PrepareRestoreSource ¶
PrepareRestoreSource returns an extracted directory for an archive source. The caller must invoke cleanup.
func ResolveBackupKeysDir ¶
ResolveBackupKeysDir returns the credential directory in an extracted credential-backup archive.
func ResolveManagedBackupPath ¶
func ResolveManagedBackupPath(paths storepaths.Paths, archivePath string) (string, error)
func RestoreKeyMetadata ¶
func RestoreKeyMetadata(keyJSON []byte) (keyType, selector string, hasLogicSigBytecode bool, err error)
RestoreKeyMetadata reads the canonical identity fields used by offline restore validation.
func ScanBackupFiles ¶
ScanBackupFiles scans a directory for .apb backup files and returns their addresses.
func WriteReadme ¶
WriteReadme writes the README.md file to the backup directory
func WriteSealedManifest ¶ added in v0.35.0
func WriteSealedManifest( destDir string, role noderole.Role, createdAt time.Time, exportPassphrase []byte, ) error
WriteSealedManifest inventories every file already staged under destDir, seals the manifest under exportPassphrase, and writes it into the archive. It must run after every other member is final: the inventory is what makes them tamper-evident.
Types ¶
type ArchiveResult ¶
type ArchiveResult struct {
ArchivePath string
ArchiveChecksum string
ArchiveSize int64
Checksums map[string]string
KeyCount int
Addresses []string
Verified bool
}
func CreateKeysArchive ¶
func CreateKeysArchive(req CreateKeysArchiveRequest) (*ArchiveResult, error)
CreateKeysArchive exports selected active keys into one tar.gz/tgz archive. When Addresses is empty, all active keys are exported.
type AuthenticatedArchiveError ¶ added in v0.35.0
type AuthenticatedArchiveError struct {
// contains filtered or unexported fields
}
AuthenticatedArchiveError marks a restore failure that occurred only after the sealed archive manifest authenticated successfully. Callers use this to avoid treating a valid export passphrase as another passphrase guess merely because a credential or destination validation failed later.
func (*AuthenticatedArchiveError) Error ¶ added in v0.35.0
func (e *AuthenticatedArchiveError) Error() string
func (*AuthenticatedArchiveError) Unwrap ¶ added in v0.35.0
func (e *AuthenticatedArchiveError) Unwrap() error
type CreateKeysArchiveRequest ¶ added in v0.35.0
type CreateKeysArchiveRequest struct {
Paths storepaths.Paths
ArchivePath string
Addresses []string
Keyring *crypto.Keyring
ExportPassphrase []byte
}
CreateKeysArchiveRequest contains one managed backup creation snapshot. Keyring and ExportPassphrase are borrowed for the duration of the call and are not cleared.
type CredentialEntry ¶ added in v0.35.0
CredentialEntry is one complete canonical managed credential prepared for destination installation. KeyJSON contains private authority and must be cleared with ZeroSecrets.
func (*CredentialEntry) ZeroSecrets ¶ added in v0.35.0
func (e *CredentialEntry) ZeroSecrets()
type ManagedBackupInfo ¶
type ManagedBackupInfo struct {
Path string
FileName string
CreatedAt time.Time
Size int64
Checksum string
}
func ListManagedBackups ¶
func ListManagedBackups(paths storepaths.Paths) ([]ManagedBackupInfo, error)
type Manifest ¶ added in v0.23.0
type Manifest struct {
Schema string `json:"schema"`
SchemaVersion int `json:"schema_version"`
SourceNodeRole string `json:"source_node_role"`
CreatedAtUnix int64 `json:"created_at_unix,omitempty"`
// Members covers every archive member except the manifest itself.
Members []ManifestMember `json:"members"`
}
Manifest is the authenticated description of a managed backup archive. Decrypting it proves the archive was created, or endorsed, by a party that knew the export passphrase (docs/PROPOSAL_ARCHIVE_MANIFEST.md); the member inventory then makes every other archive member tamper-evident.
func OpenSealedManifest ¶ added in v0.35.0
OpenSealedManifest decrypts the archive's manifest with exportPassphrase and verifies the archive against it: every listed member must be present with a matching digest and size, and no unlisted member may exist. A wrong passphrase and a tampered manifest are indistinguishable by construction (GCM), exactly as for payload content.
type ManifestMember ¶ added in v0.35.0
type ManifestMember struct {
Path string `json:"path"` // archive-relative, forward slashes
SHA256 string `json:"sha256"`
Size int64 `json:"size"`
}
ManifestMember is one archive member's authenticated identity.
type RestoreClassification ¶ added in v0.35.0
type RestoreClassification struct {
Identical []CredentialEntry
Pending []CredentialEntry
Conflicts []RestoreConflict
}
RestoreClassification is destination state pinned under the identity mutation lock immediately before generation minting.
func ClassifyRestoreSet ¶ added in v0.35.0
func ClassifyRestoreSet( active storepaths.ActivePaths, set *RestoreSet, kr *crypto.Keyring, ) (RestoreClassification, error)
ClassifyRestoreSet compares decoded canonical plaintext. An existing object that cannot be decrypted or parsed is a replaceable conflict, not an early error: this is what permits an explicitly authorized restore to repair a damaged credential while the signer remains recovery-blocked.
type RestoreConflict ¶ added in v0.35.0
type RestoreConflict struct {
Selector string
Category string
KeyType string
ExistingSHA256 string
Reason string
}
RestoreConflict describes an existing destination object that differs from an incoming canonical credential or cannot be decoded for equivalence.
type RestoreError ¶
type RestoreKeyInfo ¶
type RestoreLogger ¶
type RestorePreview ¶
type RestorePreview struct {
ArchivePath string
Keys []RestoreKeyInfo
Errors []RestoreError
}
func PreviewRestoreWithNodeRole ¶ added in v0.23.0
func PreviewRestoreWithNodeRole( paths storepaths.Paths, archivePath string, exportPassphrase []byte, role noderole.Role, ) (*RestorePreview, error)
type RestoreSet ¶ added in v0.35.0
type RestoreSet struct {
ArchiveName string
ArchiveSHA256 string
SourceNodeRole string
ArchiveCreatedAt int64
Entries []CredentialEntry
}
RestoreSet is a fully authenticated and payload-validated archive selection. It is intentionally process-local: direct restore does not publish a second destination-encrypted lifecycle outside the generation transaction.
func LoadManagedRestoreSet ¶ added in v0.35.0
func LoadManagedRestoreSet( paths storepaths.Paths, archivePath string, selectors []string, exportPassphrase []byte, role noderole.Role, ) (*RestoreSet, error)
LoadManagedRestoreSet snapshots, authenticates, decrypts, canonicalizes, and validates every selected archive credential without mutating destination storage. An empty selectors slice selects the complete archive.
func (*RestoreSet) ZeroSecrets ¶ added in v0.35.0
func (s *RestoreSet) ZeroSecrets()
type Restorer ¶
type Restorer struct {
Paths storepaths.Paths
NodeRole noderole.Role
Overwrite bool
Logf RestoreLogger
ActiveOverride storepaths.ActivePaths
}
Restorer is the narrow offline/rebuild adapter for credential-only backup entries. Live restores use LoadManagedRestoreSet and a generation mint.
func NewRestorer ¶
func NewRestorer(paths storepaths.Paths) Restorer
func (Restorer) RestoreActiveForRebuild ¶ added in v0.35.0
func (Restorer) RestoreKey ¶
func (Restorer) WithActiveNamespace ¶ added in v0.35.0
func (r Restorer) WithActiveNamespace(active storepaths.ActivePaths) Restorer
func (Restorer) WithLogger ¶
func (r Restorer) WithLogger(logf RestoreLogger) Restorer
func (Restorer) WithNodeRole ¶ added in v0.23.0
func (Restorer) WithOverwrite ¶ added in v0.34.0
type VerifyReport ¶
type VerifyReport struct {
BackupDir string
TotalFiles int
ValidFiles int
FailedFiles int
Results []VerifyResult
}
func DeepVerifyBackup ¶
func DeepVerifyBackup(backupDir, passphrase string) (*VerifyReport, error)
func DeepVerifyBackupBytes ¶ added in v0.24.0
func DeepVerifyBackupBytes(backupDir string, passphrase []byte) (*VerifyReport, error)
DeepVerifyBackupBytes authenticates the exact archive inventory and checks that every member is a self-contained, canonical credential supported by the source node role. Verification has no network or template dependency.