{"id":51687817,"url":"https://github.com/flarebyte/lunar-obsidian-crypt-go","last_synced_at":"2026-07-15T22:35:13.116Z","repository":{"id":360848533,"uuid":"1251968570","full_name":"flarebyte/lunar-obsidian-crypt-go","owner":"flarebyte","description":"The easiest way to encrypt your data with Go on the server side","archived":false,"fork":false,"pushed_at":"2026-05-28T05:00:50.000Z","size":612,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-28T06:26:46.544Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/flarebyte.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-05-28T04:23:16.000Z","updated_at":"2026-05-28T04:23:20.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/flarebyte/lunar-obsidian-crypt-go","commit_stats":null,"previous_names":["flarebyte/lunar-obsidian-crypt-go"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/flarebyte/lunar-obsidian-crypt-go","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flarebyte%2Flunar-obsidian-crypt-go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flarebyte%2Flunar-obsidian-crypt-go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flarebyte%2Flunar-obsidian-crypt-go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flarebyte%2Flunar-obsidian-crypt-go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/flarebyte","download_url":"https://codeload.github.com/flarebyte/lunar-obsidian-crypt-go/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flarebyte%2Flunar-obsidian-crypt-go/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35523587,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-15T02:00:06.706Z","response_time":131,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":[],"created_at":"2026-07-15T22:35:12.413Z","updated_at":"2026-07-15T22:35:13.111Z","avatar_url":"https://github.com/flarebyte.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# lunar-obsidian-crypt-go\n\nSign and verify application IDs as prefixed JWT tokens in Go.\n\n![Lunar Obsidian Crypt Go hero](doc/lunar-obsidian-crypt-go-hero.png)\n\nThis project is a Go port of the TypeScript library\n[`lunar-obsidian-crypt`](https://github.com/flarebyte/lunar-obsidian-crypt).\n\n## What It Does\n\n`lunar-obsidian-crypt-go` creates compact tokens for application resource IDs:\n\n```text\nprefix:jwt-token\n```\n\nThe prefix selects the signing configuration. The JWT payload is signed with an\nHMAC algorithm and can later be verified with the same configured prefix.\n\nUse it when you need to:\n\n- sign IDs such as product, company, tenant, account, or internal resource IDs\n- attach optional scope to a token\n- verify that a token was signed by a configured secret\n- rotate secrets with a current and previous secret\n- return structured success or failure values instead of panics or exceptions\n\n## Security Boundary\n\nThis library signs data; it does not encrypt data.\n\nJWT payloads are visible to anyone who holds the token. Do not put passwords,\nAPI keys, secrets, or private user data in the payload. Store only identifiers\nand scope values that are safe to reveal.\n\n## Installation\n\n```bash\ngo get github.com/flarebyte/lunar-obsidian-crypt-go\n```\n\nThe JWT implementation dependency is\n[`github.com/golang-jwt/jwt/v5`](https://pkg.go.dev/github.com/golang-jwt/jwt/v5).\n\n## Basic Usage\n\nCreate a store with one or more prefixes. Plain `Store` structs are the most\ntransparent setup path and work well in tests:\n\n```go\npackage main\n\nimport (\n\t\"log\"\n\n\tlunarcrypt \"github.com/flarebyte/lunar-obsidian-crypt-go\"\n)\n\nfunc main() {\n\tcrypt, err := lunarcrypt.New(lunarcrypt.Store{\n\t\tTitle: \"Business ID signing store\",\n\t\tCyphers: map[string]lunarcrypt.TranslucentLizardCypher{\n\t\t\t\"product\": {\n\t\t\t\tKind:       lunarcrypt.TranslucentLizard,\n\t\t\t\tTitle:      \"Sign product IDs\",\n\t\t\t\tSecret:     []byte(\"replace-with-a-long-random-secret\"),\n\t\t\t\tStrength:   lunarcrypt.Sufficient,\n\t\t\t\tExpiration: lunarcrypt.Expiration{Value: 2, Unit: lunarcrypt.Hours},\n\t\t\t},\n\t\t\t\"company\": {\n\t\t\t\tKind:          lunarcrypt.TranslucentLizard,\n\t\t\t\tTitle:         \"Sign company IDs\",\n\t\t\t\tSecret:        []byte(\"replace-with-another-long-random-secret\"),\n\t\t\t\tAltSecret:     []byte(\"replace-with-previous-secret-during-rotation\"),\n\t\t\t\tStrength:      lunarcrypt.Strong,\n\t\t\t\tExpiration:    lunarcrypt.Expiration{Value: 2, Unit: lunarcrypt.Weeks},\n\t\t\t\tExpectedScope: map[string]lunarcrypt.ScopeValue{\"account\": {\"account890\"}},\n\t\t\t},\n\t\t},\n\t})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t_ = crypt\n}\n```\n\nThe builder API is a guided setup path. It produces the same plain `Store`\nmodel, which is still validated by `New`:\n\n```go\nstore, err := lunarcrypt.NewBuilder().\n\tSetTitle(\"Business ID signing store\").\n\tAddTranslucentLizard(\"product\", lunarcrypt.TranslucentLizardCypher{\n\t\tKind:       lunarcrypt.TranslucentLizard,\n\t\tTitle:      \"Sign product IDs\",\n\t\tSecret:     []byte(\"replace-with-a-long-random-secret\"),\n\t\tStrength:   lunarcrypt.Sufficient,\n\t\tExpiration: lunarcrypt.Expiration{Value: 2, Unit: lunarcrypt.Hours},\n\t}).\n\tBuild()\nif err != nil {\n\tlog.Fatal(err)\n}\n\ncrypt, err := lunarcrypt.New(store)\nif err != nil {\n\tlog.Fatal(err)\n}\n```\n\n## Sign An ID\n\n```go\npayload := lunarcrypt.IDPayload{\n\tID: \"product123\",\n\tScope: map[string]lunarcrypt.ScopeValue{\n\t\t\"account\": {\"account890\"},\n\t},\n}\n\nsignResult := crypt.SignID(\"product\", payload)\nif signResult.Status == lunarcrypt.Failure {\n\tlog.Printf(\"sign failed at %s: %s\", signResult.Error.Step, signResult.Error.Message)\n\treturn\n}\n\nfullToken := signResult.Value\n```\n\n`fullToken` will use the prefixed token format:\n\n```text\nproduct:eyJ...\n```\n\n## Verify A Token\n\n```go\nverifyResult := crypt.VerifyID(fullToken)\nif verifyResult.Status == lunarcrypt.Failure {\n\tlog.Printf(\"verify failed at %s: %s\", verifyResult.Error.Step, verifyResult.Error.Message)\n\treturn\n}\n\npayload := verifyResult.Value\n```\n\nSuccessful verification returns the application payload without the JWT `exp`\nclaim.\n\n## Verify A Specific Prefix\n\nUse `VerifyIDByPrefix` when the caller already knows which prefix must be used:\n\n```go\nverifyResult := crypt.VerifyIDByPrefix(\"product\", fullToken)\nif verifyResult.Status == lunarcrypt.Failure {\n\tlog.Printf(\"verify failed at %s\", verifyResult.Error.Step)\n\treturn\n}\n```\n\nIf the embedded token prefix does not match the requested prefix, verification\nfails.\n\n## Scope Checks\n\nA cypher can require scope values:\n\n```go\nExpectedScope: map[string]lunarcrypt.ScopeValue{\n\t\"account\": {\"account890\"},\n}\n```\n\nDuring verification, every configured expected scope key must be present and\nmust match the decoded payload scope by value.\n\nFor custom policy, attach a validator:\n\n```go\nScopeValidator: func(scope map[string]lunarcrypt.ScopeValue) error {\n\tif len(scope[\"account\"]) == 0 {\n\t\treturn fmt.Errorf(\"account is required\")\n\t}\n\treturn nil\n}\n```\n\n## Secret Rotation\n\nSet `AltSecret` during rotation:\n\n```go\nTranslucentLizardCypher{\n\tSecret:    []byte(\"current-secret\"),\n\tAltSecret: []byte(\"previous-secret\"),\n}\n```\n\nVerification tries the current secret first. If that fails, it tries the\nprevious secret. Signing always uses the current secret.\n\n## Strength Mapping\n\n| Strength | JWT algorithm |\n| --- | --- |\n| `Sufficient` | `HS256` |\n| `Good` | `HS384` |\n| `Strong` | `HS512` |\n\n## Result Shape\n\nExpected signing and verification failures return `Result[T]` values:\n\n```go\ntype Result[T any] struct {\n\tStatus ResultStatus\n\tValue  T\n\tError  *CryptError\n}\n```\n\nConfiguration errors are returned by `New(store)` as Go errors because they are\nsetup failures and should be fixed before runtime use.\n\n## Error Steps\n\nCallers can branch on stable error step IDs:\n\n| Step | Meaning |\n| --- | --- |\n| `sign-id/store` | signing prefix or cypher is not configured |\n| `sign-id/validate-payload` | payload is invalid before signing |\n| `sign-id/sign` | JWT signing failed |\n| `verify-id/extract-token` | full token prefix or token syntax is invalid |\n| `verify-id/store` | verification prefix or cypher is not configured |\n| `verify-id/decode-token` | token decoding failed |\n| `verify-id/validate-payload` | decoded payload shape is invalid |\n| `verify-id/verify-scope` | expected or custom scope check failed |\n| `verify-id/verify-token` | signature, expiration, or JWT verification failed |\n\n## Design Specification\n\nThe protocol and Go implementation notes are maintained in `doc/design-meta`\nand generated with:\n\n```bash\nmake doc-design\n```\n\nRead the generated specification at\n[`doc/design/lunar-obsidian-crypt-spec.md`](doc/design/lunar-obsidian-crypt-spec.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflarebyte%2Flunar-obsidian-crypt-go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fflarebyte%2Flunar-obsidian-crypt-go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflarebyte%2Flunar-obsidian-crypt-go/lists"}