TazPod: S3 Detail
Level 3 (Detail) — AWS S3 client for vault persistence.
Concept
The S3 client provides remote vault persistence to Amazon S3. It supports upload, download, and bucket creation. The default target is bucket tazlab-storage in eu-central-1.
Client Initialization
File: internal/utils/s3.go — NewS3Client()
func NewS3Client(awsAccessKeyId, awsSsoProfile string) (*S3Client, error)
- If
awsAccessKeyIdis set → uses static credentials directly - Otherwise → uses the AWS SSO profile from
cfg.AwsSso.Profile - Falls back to
AWS_ACCESS_KEY_IDenv var if both are empty - Region is hardcoded to
eu-central-1
Methods
UploadFile
func (s *S3Client) UploadFile(key, filePath string) error
key: S3 object key (e.g.,tazpod/vault/vault.tar.aes)filePath: local file path to upload- Uses
s3manager.NewUploader
DownloadFile
func (s *S3Client) DownloadFile(key, filePath string) error
key: S3 object keyfilePath: local destination path- Uses
s3manager.NewDownloader
CreateBucket
func (s *S3Client) CreateBucket() error
- Creates
tazlab-storagebucket if it doesn’t exist - Called by
setupStorage()command
Vault Object Contract
| Property | Value |
|---|---|
| Bucket | tazlab-storage |
| Region | eu-central-1 |
| Latest key | tazpod/vault/vault.tar.aes |
| History prefix | tazpod/vault/history/vault-<TIMESTAMP>.tar.aes |
| Local path | .tazpod/vault/vault.tar.aes |
History Copies (v0.4+)
Every tazpod push vault creates a versioned history copy if the vault content has actually changed. Detection is via SHA256 of the plaintext (before encryption), stored as S3 metadata x-amz-meta-content-sha256 on both the latest and history objects.
tazpod/vault/vault.tar.aes ← Latest (overwrite, with content-sha256 metadata)
tazpod/vault/history/vault-20260520T210000.tar.aes ← History copy (with content-sha256 metadata)
tazpod/vault/history/vault-20260520T200000.tar.aes ← History copy
...
Retention: max N=50 history copies (configurable via vault.retention). Pruning is automatic on push.
Path Resolution (TD-022 — Fixed in v0.4+)
tazpod push vault and tazpod pull vault now try both paths and use the first that exists:
func vaultFilePath() string {
paths := []string{
"/workspace/.tazpod/vault/vault.tar.aes", // inside container
".tazpod/vault/vault.tar.aes", // on host (relative to cwd)
}
for _, p := range paths {
if _, err := os.Stat(p); err == nil { return p }
}
return paths[0]
}
This works from both the container and the host regardless of current directory.
S3 Methods
| Method | Description |
|---|---|
UploadFile(key, filePath) | Upload file to S3 (plain, no metadata) |
UploadFileWithMetadata(key, filePath, metadata) | Upload with custom metadata map |
DownloadFile(key, filePath) | Download from S3 to local file |
HeadObject(key) | Get object metadata without downloading |
ListObjects(prefix) | List objects by prefix, returns []ObjectInfo{Key, LastModified, Size} |
DeleteObjects(keys) | Batch delete up to 1000 keys |
Lineage Concept
The S3 integration supports a lineage-based versioning scheme (used by the Hetzner Vault runtime for deterministic restore):
tazpod/vault/vault.tar.aes— latest vault- Multiple slot-a / slot-b pointers for lineage tracking
See Also
- Parent hub: tazpod
- Sibling details: Vault Lifecycle Detail, Sync Daemon Detail
- Debt: TD-022 (cwd-sensitive path resolution)