{"id":49103153,"url":"https://github.com/vmvarela/ghoten-oras-backend","last_synced_at":"2026-04-21T00:11:47.315Z","repository":{"id":350742580,"uuid":"1208076293","full_name":"vmvarela/ghoten-oras-backend","owner":"vmvarela","description":"A Go library that stores Terraform/OpenTofu state in container registries using the ORAS/OCI standard.","archived":false,"fork":false,"pushed_at":"2026-04-11T20:18:02.000Z","size":53,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-04-11T22:09:57.736Z","etag":null,"topics":["opentofu","oras","terraform"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vmvarela.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-11T19:35:26.000Z","updated_at":"2026-04-11T20:20:54.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/vmvarela/ghoten-oras-backend","commit_stats":null,"previous_names":["vmvarela/ghoten-oras-backend"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/vmvarela/ghoten-oras-backend","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vmvarela%2Fghoten-oras-backend","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vmvarela%2Fghoten-oras-backend/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vmvarela%2Fghoten-oras-backend/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vmvarela%2Fghoten-oras-backend/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vmvarela","download_url":"https://codeload.github.com/vmvarela/ghoten-oras-backend/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vmvarela%2Fghoten-oras-backend/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32071067,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-20T21:26:33.338Z","status":"ssl_error","status_checked_at":"2026-04-20T21:26:22.081Z","response_time":94,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["opentofu","oras","terraform"],"created_at":"2026-04-21T00:11:47.257Z","updated_at":"2026-04-21T00:11:47.309Z","avatar_url":"https://github.com/vmvarela.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ghoten-oras-backend\n\nA standalone Go library for storing Terraform/OpenTofu state as OCI artifacts in container registries using the [ORAS](https://oras.land/) protocol.\n\n## Features\n\n- **Provider-agnostic** — works with both Terraform and OpenTofu\n- **Registry support** — GHCR, Zot, and any OCI-compliant registry\n- **Workspace management** — list, create, delete workspaces via manifest tags\n- **State locking** — generation-based optimistic locking with configurable TTL\n- **Versioning** — configurable state version retention with async cleanup\n- **Compression** — optional gzip compression for state payloads\n- **GHCR fallback** — handles GHCR's lack of tag deletion via GitHub API\n- **Retry \u0026 rate limiting** — built-in exponential backoff and per-second rate limiting\n- **OpenTelemetry** — HTTP transport instrumented with otelhttp\n- **No encryption** — raw `[]byte` in/out; encryption belongs in the caller\n\n## Installation\n\n```bash\ngo get github.com/vmvarela/ghoten-oras-backend\n```\n\nRequires **Go 1.25+**.\n\n## Quick Start\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/vmvarela/ghoten-oras-backend/backend/oras\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tbackend, err := oras.New(ctx, oras.Config{\n\t\tRepository: \"ghcr.io/myorg/myrepo\",\n\t\tCredentialFunc: func(ctx context.Context, host string) (oras.Credential, error) {\n\t\t\treturn oras.Credential{\n\t\t\t\tUsername: \"myuser\",\n\t\t\t\tPassword: \"mytoken\",\n\t\t\t}, nil\n\t\t},\n\t})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer backend.Close()\n\n\t// List workspaces\n\tworkspaces, err := backend.Workspaces(ctx)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tfmt.Println(\"Workspaces:\", workspaces)\n\n\t// Get a state manager for the default workspace\n\tmgr, err := backend.StateMgr(ctx, oras.DefaultStateName)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Read current state\n\tpayload, err := mgr.Get(ctx)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tif payload != nil {\n\t\tfmt.Printf(\"State size: %d bytes\\n\", len(payload.Data))\n\t}\n}\n```\n\n## API\n\n### `oras.New`\n\n```go\nfunc New(ctx context.Context, cfg Config) (StateBackend, error)\n```\n\nCreates a new ORAS backend. Validates the config and establishes the OCI repository connection.\n\n### `StateBackend`\n\n```go\ntype StateBackend interface {\n    Workspaces(ctx context.Context) ([]string, error)\n    DeleteWorkspace(ctx context.Context, name string, force bool) error\n    StateMgr(ctx context.Context, workspace string) (StateMgr, error)\n    Close() error\n}\n```\n\n### `StateMgr`\n\n```go\ntype StateMgr interface {\n    Get(ctx context.Context) (*StatePayload, error)\n    Put(ctx context.Context, data []byte) error\n    Delete(ctx context.Context) error\n    Lock(ctx context.Context, info *LockInfo) (string, error)\n    Unlock(ctx context.Context, id string) error\n    WaitForRetention()\n}\n```\n\n### `Config`\n\n| Field | Type | Default | Description |\n|-------|------|---------|-------------|\n| `Repository` | `string` | — | OCI repository (e.g. `ghcr.io/org/repo`). **Required.** |\n| `Insecure` | `bool` | `false` | Skip TLS verification. |\n| `CAFile` | `string` | `\"\"` | Path to PEM CA certificate bundle. |\n| `Compression` | `string` | `\"none\"` | `\"none\"` or `\"gzip\"`. |\n| `LockTTL` | `time.Duration` | `0` | Auto-clear stale locks older than this. |\n| `RateLimit` | `int` | `0` | Max registry requests/second (0 = unlimited). |\n| `RateBurst` | `int` | `0` | Rate limiter burst size. |\n| `Retry` | `RetryConfig` | 3 attempts, 1s–30s | Retry config for transient errors. |\n| `MaxStateSize` | `int64` | `256 MiB` | Max state payload size. |\n| `MaxVersions` | `int` | `0` | State version retention (0 = disabled). |\n| `UserAgent` | `string` | `\"\"` | Custom HTTP User-Agent. |\n| `CredentialFunc` | `CredentialFunc` | `nil` | Returns credentials per registry host. |\n\n### `RetryConfig`\n\n```go\ntype RetryConfig struct {\n    MaxAttempts       int           // Default: 3\n    InitialBackoff    time.Duration // Default: 1s\n    MaxBackoff        time.Duration // Default: 30s\n    BackoffMultiplier float64       // Default: 2.0\n}\n```\n\n### Types\n\n```go\ntype StatePayload struct {\n    Data []byte\n    MD5  []byte\n}\n\ntype LockInfo struct {\n    ID        string\n    Operation string\n    Info      string\n    Who       string\n    Version   string\n    Created   time.Time\n    Path      string\n}\n\ntype LockError struct {\n    Info             *LockInfo\n    Err              error\n    InconsistentRead bool\n}\n\ntype Credential struct {\n    Username    string\n    Password    string\n    AccessToken string\n}\n\ntype CredentialFunc func(ctx context.Context, hostport string) (Credential, error)\n```\n\n## Locking\n\nState locking uses generation-based optimistic concurrency:\n\n1. **Lock** pushes a manifest with `LockInfo` as a JSON annotation, tagged `lock-\u003cworkspace\u003e`.\n2. The manifest includes the current generation (resolved from the existing lock tag). If the tag moved since resolution, the push fails — another holder acquired the lock.\n3. **Unlock** deletes the lock manifest (or tags an \"unlocked\" sentinel on GHCR where tag deletion is unsupported).\n4. **LockTTL** — when set, stale locks older than the TTL are automatically cleared during lock acquisition.\n\n## Versioning\n\nWhen `MaxVersions \u003e 0`, each `Put` creates a versioned snapshot tagged `state-\u003cworkspace\u003e-v\u003cN\u003e`. A background goroutine prunes versions exceeding the retention limit. Call `WaitForRetention()` before process exit to ensure cleanup completes.\n\n## GHCR Compatibility\n\nGitHub Container Registry doesn't support the OCI tag deletion API. This library includes a fallback that uses the GitHub Packages REST API to delete package versions by tag. The fallback activates automatically when the registry host is `ghcr.io`.\n\n## License\n\nSee [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvmvarela%2Fghoten-oras-backend","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvmvarela%2Fghoten-oras-backend","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvmvarela%2Fghoten-oras-backend/lists"}