{"id":37132755,"url":"https://github.com/darmiel/talmi","last_synced_at":"2026-01-14T15:29:21.212Z","repository":{"id":329140903,"uuid":"1098615490","full_name":"darmiel/talmi","owner":"darmiel","description":"Minimal STS that grants access to downstream resources based on identities from trusted upstream IdPs.","archived":false,"fork":false,"pushed_at":"2025-12-19T09:26:55.000Z","size":163,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-21T03:13:50.185Z","etag":null,"topics":["oidc","oidc-federation","sts"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/darmiel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2025-11-17T23:28:15.000Z","updated_at":"2025-12-19T09:26:59.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/darmiel/talmi","commit_stats":null,"previous_names":["darmiel/talmi"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/darmiel/talmi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darmiel%2Ftalmi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darmiel%2Ftalmi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darmiel%2Ftalmi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darmiel%2Ftalmi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/darmiel","download_url":"https://codeload.github.com/darmiel/talmi/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darmiel%2Ftalmi/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28424374,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T13:30:50.153Z","status":"ssl_error","status_checked_at":"2026-01-14T13:29:08.907Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5: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":["oidc","oidc-federation","sts"],"created_at":"2026-01-14T15:29:20.526Z","updated_at":"2026-01-14T15:29:21.204Z","avatar_url":"https://github.com/darmiel.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Talmi\n\nTalmi is a small Security Token Service (STS) written in Go.\n\nIt verifies upstream identity tokens (usually OIDC JWTs), evaluates them against policy rules, and mints short-lived\ndownstream artifacts (for example GitHub App tokens). Talmi can record audit logs and keep a registry of currently\nactive (non-expired) tokens.\n\n## Why Talmi exists\n\nWorkloads (CI jobs, automation, services) often need credentials to call APIs. Long-lived secrets in build systems are\nhard to protect and hard to audit.\n\nWith OIDC federation, the workload gets a short-lived identity token from an upstream issuer (e.g. GitHub Actions) that\nproves its identity. Talmi can then verify this identity, check it against defined policies, and mint short-lived,\nscoped credentials for the requested resource.\n\n## How Talmi works\n\nThe basic flow to issue an artifact (=\u003e token) is as follows:\n\n* Receive an upstream token (OIDC JWT).\n* Verify the token and build a principal (subject + claims).\n* Evaluate rules in order. **First match wins.**\n* Mint an artifact (=\u003e token) using the rule’s provider configuration.\n* Audit\n\n---\n\n### Quick Start\n\nYou can install the Talmi CLI and server using `go install`:\n\n```bash\ngo install github.com/darmiel/talmi@latest\n```\n\nCreate a configuration file `talmi.yaml` - you can find an example in the\n[`examples/`](examples/) directory.\n\nThen start the Talmi server:\n\n```bash\ntalmi serve --config talmi.yaml --addr :8080\n```\n\nTo perform administrative tasks, you must first authenticate with Talmi using a token that matches a rule granting\naccess to the `talmi` provider. You can find an example how to do so in\n[`examples/talmi-testing.yaml`](examples/talmi-testing.yaml) (see `talmi-jwt` provider).\n\n---\n\n## Concepts\n\n### Issuer\n\nAn issuer is a trusted source of upstream identity tokens. In most setups this is OIDC (Talmi fetches signing keys via\nthe issuer’s discovery endpoints).\n\nThe issuer configuration determines:\n\n* which token sources are trusted\n* which audience/client ID is expected\n* how verification happens\n\n```yaml\nissuers:\n  - name: name-of-issuer\n    type: oidc\n    issuer_url: https://issuer.example.com/\n    client_id: my-audience\n```\n\n\u003e [!IMPORTANT]\n\u003e The Talmi server requires network access to the Issuer's URL\n\u003e (specifically the `.well-known/openid-configuration` endpoint) to fetch public keys for signature verification.\n\n### Principal\n\nAfter verification Talmi represents the caller as a principal:\n\n* `principal.id` (usually the token subject)\n* `principal.issuer` (which issuer verified the token)\n* `principal.attributes` (claims from the token, e.g. sub, repository, workflow, etc.)\n\nRules match against these attributes.\n\n### Rule\n\nRules are policy entries that map identity to a grant.\n\n**Important properties:**\n\n* Rules are evaluated top to bottom\n* First matching rule wins\n* A rule has:\n    * `match`: issuer + conditions\n    * `grant`: which provider to mint from (and its config)\n\n**A minimal rule shape:**\n\n```yaml\n- name: allow-something\n  match:\n    issuer: my-oidc\n    condition: ...\n  grant:\n    provider: github\n    config: ...\n```\n\n### Provider\n\nProviders mint the downstream artifact. Talmi’s policy engine decides which provider and how it should mint, but the\nprovider decides the concrete “artifact” format (e.g., GitHub installation token, Talmi admin JWT, etc.).\n\n**Currently supported providers:** (check [provider/registry.go](internal/providers/registry.go) for the full list)\n\n* `stub`: returns dummy tokens for testing and writes to log\n* `github-app`: mints GitHub App installation tokens. For configuration details\n  see [`GitHubAppProviderConfig`](internal/providers/github_app.go)\n  and [`GitHubAppGrantConfig`](internal/providers/github_app.go)\n* `talmi-jwt`: mints Talmi admin JWTs for accessing Talmi server admin APIs.\n  For configuration details see [`TalmiJWTProviderConfig`](internal/providers/talmi_jwt.go)\n  and [`TalmiJWTGrantConfig`](internal/providers/talmi_jwt.go)\n\nYou can configure multiple providers of the same kind to enforce the **Principle of Least Privilege**.\n\nFor example, you might configure two GitHub providers:\n\n1. `github-ci-reader`: A GitHub App with only \"Read-Only\" permissions, available to all CI pipelines.\n2. `github-admin`: A GitHub App with \"Write\" permissions, restricted to a specific list of repository owners via Policy\n   Rules.\n\nThis means you don't need to create a single, over-privileged GitHub App. Instead, you can create multiple Apps with\nspecific permission sets and control access via Talmi's policy engine.\n\n---\n\n## Writing match conditions\n\nTalmi supports simple matchers for common cases, plus composition for more complex logic.\n\n### Simple Matchers\n\n```yaml\nmatch:\n  issuer: flower-oidc\ncondition:\n  sub: { contains: \"@company.com\" }\n```\n\nSupported operators (from the existing README/code intent):\n\n* `equals`\n* `contains`\n* `in`\n* `exists`\n* for a full list see [condition.go](internal/core/condition.go)\n\n### Combine conditions\n\nUse `all` (AND), `any` (OR), and `not`:\n\n```yaml\nmatch:\n  issuer: flower-oidc\n  conditions:\n    all: # all following conditions must match\n      - user: { contains: \"@company.com\" }\n      - not: { user: \"bob@company.com\" } # what did you do, Bob?\n```\n\n\u003e [!TIP]\n\u003e If you cannot use the short syntax, for example when your attribute name collides with one of the operators, you can use\n\u003e the long syntax:\n\u003e\n\u003e ```yaml\n\u003e condition:\n\u003e   key: all\n\u003e   operator: equals\n\u003e   value: some-value\n\u003e ```\n\n**Keep rules ordered from most specific to most general.**\n\n## Debugging Policies\n\nBefore writing rules, confirm what claims your upstream tokens actually contain. Talmi\nincludes a helper (`talmi attributes`) that decodes JWT claims without validating them.\nUse it to learn the available attribute names and values, then write match conditions against those.\n\n### Use `why` when rules don’t behave as expected\n\n`why` produces a structured trace of each rule and condition result. It’s the fastest way to see:\n\n* which rule first matched\n* exactly which condition failed and why\n* whether issuer matching/selection is the issue\n\n`why` can run either:\n\n* **locally** against a config file (useful while editing policies) by providing `-f talmi.yaml`\n* **remotely** against a server where you are authenticated as `admin` (note that this does not produce an audit log\n  entry)\n\n\u003e [!TIP]\n\u003e You view the evaluation trace for a previous request by providing the correlation ID you can find in the audit log:\n\u003e ```bash\n\u003e talmi why --replay-id \u003ccorrelation-id\u003e\n\u003e ```\n\u003e Note that this applies the _current_ policy configuration, not the one that was active at the time of the request.\n\n\u003cimg width=\"955\" height=\"455\" alt=\"Policy Trace Output\" src=\"https://github.com/user-attachments/assets/4929292a-a4d0-487a-a449-233dbbebe7b8\" /\u003e\n\n---\n\n## Auditing\n\nTalmi provides built-in tools to track access and security events.\n\n### Active Tokens\n\nThe token registry tracks all currently valid tokens issued by the system. You can list them using:\n\n```bash\ntalmi audit tokens\n```\n\n\u003cimg width=\"1388\" height=\"231\" alt=\"Image\" src=\"https://github.com/user-attachments/assets/c3d32fda-75a6-41bd-b358-d45e10a04b40\" /\u003e\n\n### Audit Log\n\nThe audit log records every access request, including denied requests:\n\n```bash\ntalmi audit log --limit 50\n```\n\n\u003cdetails\u003e\n    \u003csummary\u003eIt contains time, correlation-id, principal and status\u003c/summary\u003e\n\n```\n╭───────────────────────────┬──────────────────────┬────────────────────────────┬───────────────╮\n│ Time                      │ Correlation ID       │ Principal                  │ Action        │\n├───────────────────────────┼──────────────────────┼────────────────────────────┼───────────────┤\n│ 2025-12-17T22:55:47+01:00 │ d51idota26vcnr2h29ag │ daniel@company.com         │ ✔ issue_token │\n│ 2025-12-17T23:01:46+01:00 │ d51igila26vcnr2h29b0 │ my-pipeline/my-job/my-task │ ✔ issue_token │\n╰───────────────────────────┴──────────────────────┴────────────────────────────┴───────────────╯\n```\n\n\u003c/details\u003e\n\nTo view full details of a specific audit entry (=\u003e request), including metadata and errors, use:\n\n```bash\ntalmi audit inspect \u003caudit-id\u003e\n```\n\n\u003cdetails\u003e\n    \u003csummary\u003eThis shows more information about this audit entry\u003c/summary\u003e\n\n```\n\n── Audit Entry ──\n  Correlation ID:            d51igila26vcnr2h29b0\n  Time:                      Wed, 17 Dec 2025 23:01:46 CET\n  Action:                    issue_token\n  Decision:                  granted\n\n── Identity ──\n  Subject:                   my-pipeline/my-job/my-task\n  Issuer:                    flower-oidc\n  Action:                    issue_token\n  Attributes:                \n       aud:             [talmi-dev]\n       exp:             1.766012497e+09\n       iat:             1.766008897e+09\n       intent:          create\n       iss:             https://flower.d2a.io/oidc\n       sub:             my-pipeline/my-job/my-task\n\n── Request \u0026 Policy ──\n  Action:                    issue_token\n  Req. Provider:             (first grant)\n  Req. Issuer:               (auto discover)\n  Matched Rule:              allow-job-to-create-repositories\n\n── Output ──\n  Provider:                  github\n  Fingerprint:               SQHSEnPLrtM2agiC5M4kLD+M5Wxzb4DXXd0OobIioaQ=\n  Metadata:                  \n       installation:    25508\n       permissions:     map[actions:write]\n       repositories:    \u003cnil\u003e\n```\n\n\u003c/details\u003e\n\n\u003cimg width=\"1612\" height=\"274\" alt=\"Active Tokens Table\" src=\"https://github.com/user-attachments/assets/b0a2816d-a543-49c7-a94b-5aa2c35555fe\" /\u003e\n\n### Token Fingerprinting\n\nAll minting requests should contain the correlation ID by passing it as the `User-Agent` for requests in the following\nformat:\n\n```\nTalmi/v1.0.0 (correlation_id=%s; principal=%s; provider=%s)\n```\n\nAdditionally, Talmi may compute a _fingerprint_ for each minted token (depending on the provider).\nThis allows you to track token usage in audit logs of downstream services (e.g., GitHub).\n\nYou can find more information about identifying audit log events in\nGitHub [here](https://docs.github.com/en/enterprise-cloud@latest/admin/monitoring-activity-in-your-enterprise/reviewing-audit-logs-for-your-enterprise/identifying-audit-log-events-performed-by-an-access-token)\n\nOne common use-case: you see activity in a downstream system (e.g., GitHub Enterprise audit log) and want to find the\nexact decision that produced the token.\n\n#### Example: trace a GitHub token back to the Talmi decision\n\n1. Copy the token fingerprint/hash from the downstream audit log\n2. Search Talmi audit log entries for that fingerprint\n3. Use Talmi’s `audit log --fingerprint=\u003cfingerprint\u003e` to locate the matching request(s).\n4. Take the `Correlation ID` from the matching audit entry\n   The correlation ID is the stable handle you use inside Talmi to inspect a single request in detail.\n5. Inspect the full audit entry\n   This shows the principal attributes, requested provider/issuer, matched rule (if any), provider output, and\n   metadata/errors.\n6. Run an evaluation trace (`why --replay-id=\u003ccorrelation-id\u003e`) to see the rule-by-rule evaluation trace. The trace is\n   useful when:\n    * a rule matched unexpectedly\n    * a rule did not match when you thought it would\n    * the wrong provider was chosen due to rule order\n    * a condition failed due to missing/renamed claims\n\n---\n\n### Architecture\n\n```mermaid\nsequenceDiagram\n    participant Client as CI Pipeline / Client\n    participant IdP as Identity Provider (e.g., GitHub Actions)\n    participant Talmi as Talmi Server\n    participant Provider as Downstream Provider (e.g., GitHub App)\n\n    Note over Client, IdP: 1. Identity\n    Client-\u003e\u003eIdP: Request OIDC Token\n    IdP--\u003e\u003eClient: Signed JWT\n\n    Note over Client, Talmi: 2. Exchange\n    Client-\u003e\u003eTalmi: Request Access (send JWT)\n    \n    Note over Talmi: 3. Policy Engine\n    Talmi-\u003e\u003eTalmi: Verify JWT Signature\n    Talmi-\u003e\u003eTalmi: Match Principal against Rules\n    \n    Note over Talmi, Provider: 4. Minting\n    Talmi-\u003e\u003eProvider: Request Scoped Token\n    Provider--\u003e\u003eTalmi: Short-lived Access Token\n\n    Talmi--\u003e\u003eClient: Return Access Token\n```\n\n---\n\n... README is under construction ...\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdarmiel%2Ftalmi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdarmiel%2Ftalmi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdarmiel%2Ftalmi/lists"}