{"id":45029813,"url":"https://github.com/emfga/tsfga","last_synced_at":"2026-02-19T05:00:58.303Z","repository":{"id":339102381,"uuid":"1158940875","full_name":"emfga/tsfga","owner":"emfga","description":"TypeScript implementation of OpenFGA-compatible relationship-based access control (ReBAC).","archived":false,"fork":false,"pushed_at":"2026-02-18T22:56:57.000Z","size":503,"stargazers_count":0,"open_issues_count":3,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-19T02:19:52.906Z","etag":null,"topics":["authorization","bun","deno","kysely","nodejs","openfga","rbac","rebac","typescript","zanzibar"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/emfga.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-02-16T05:30:31.000Z","updated_at":"2026-02-18T22:56:58.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/emfga/tsfga","commit_stats":null,"previous_names":["tsfga/tsfga","emfga/tsfga"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/emfga/tsfga","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emfga%2Ftsfga","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emfga%2Ftsfga/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emfga%2Ftsfga/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emfga%2Ftsfga/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/emfga","download_url":"https://codeload.github.com/emfga/tsfga/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emfga%2Ftsfga/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29604095,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-19T04:38:07.383Z","status":"ssl_error","status_checked_at":"2026-02-19T04:35:50.016Z","response_time":117,"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":["authorization","bun","deno","kysely","nodejs","openfga","rbac","rebac","typescript","zanzibar"],"created_at":"2026-02-19T05:00:42.584Z","updated_at":"2026-02-19T05:00:58.275Z","avatar_url":"https://github.com/emfga.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tsfga\n\nTypeScript implementation of OpenFGA-compatible relationship-based access\ncontrol (ReBAC).\n\n## Features\n\n- **5-step recursive check algorithm** — direct tuples, userset expansion,\n  relation inheritance, computed usersets, and tuple-to-userset\n- **CEL condition evaluation** — conditional tuple access via\n  `@marcbachmann/cel-js`\n- **Database-agnostic core** — the check algorithm depends only on a `TupleStore`\n  interface\n- **Kysely adapter** — PostgreSQL implementation included out of the box\n- **Conformance-tested** — validated against a real OpenFGA service to ensure\n  identical results\n\n## Architecture\n\n```\ncreateTsfga (public API)\n  ↓\ncheck / conditions (core algorithm)\n  ↓\nTupleStore (interface)\n  ↓\nKyselyTupleStore (adapter)\n```\n\nThe `@tsfga/core` package contains pure logic with no database dependencies.\nIt communicates with storage through the `TupleStore` interface, which the\n`@tsfga/kysely` adapter implements for PostgreSQL.\n\n## Installation\n\n```bash\n# Core library (check algorithm, types, conditions)\nnpm install @tsfga/core\n\n# PostgreSQL adapter (requires Kysely and pg as peer deps)\nnpm install @tsfga/kysely kysely pg\n```\n\n## Quick start\n\n```typescript\nimport { createTsfga } from \"@tsfga/core\";\nimport { KyselyTupleStore } from \"@tsfga/kysely\";\nimport { Kysely, PostgresDialect } from \"kysely\";\nimport Pool from \"pg-pool\";\n\nconst db = new Kysely({\n  dialect: new PostgresDialect({ pool: new Pool({ connectionString: \"...\" }) }),\n});\n\nconst store = new KyselyTupleStore(db);\nconst fga = createTsfga(store);\n\n// Write relation configs (typically derived from your authorization model)\nawait fga.writeRelationConfig({\n  objectType: \"document\",\n  relation: \"viewer\",\n  directlyAssignableTypes: [\"user\"],\n  allowsUsersetSubjects: false,\n});\n\n// Add a tuple\nawait fga.addTuple({\n  objectType: \"document\",\n  objectId: \"550e8400-e29b-41d4-a716-446655440000\",\n  relation: \"viewer\",\n  subjectType: \"user\",\n  subjectId: \"7c9e6679-7425-40de-944b-e07fc1f90ae7\",\n});\n\n// Check access\nconst allowed = await fga.check({\n  objectType: \"document\",\n  objectId: \"550e8400-e29b-41d4-a716-446655440000\",\n  relation: \"viewer\",\n  subjectType: \"user\",\n  subjectId: \"7c9e6679-7425-40de-944b-e07fc1f90ae7\",\n});\n// → true\n```\n\n## API\n\n`createTsfga(store, options?)` returns an `TsfgaClient` with the following methods:\n\n| Method | Description |\n|---|---|\n| `check(request)` | Check if a subject has a relation on an object |\n| `addTuple(request)` | Insert or update a relationship tuple |\n| `removeTuple(request)` | Delete a relationship tuple |\n| `listObjects(objectType, relation, subjectType, subjectId)` | List object IDs the subject can access |\n| `listSubjects(objectType, objectId, relation)` | List direct subjects for an object + relation |\n| `writeRelationConfig(config)` | Insert or update a relation configuration |\n| `deleteRelationConfig(objectType, relation)` | Delete a relation configuration |\n| `writeConditionDefinition(condition)` | Insert or update a CEL condition definition |\n| `deleteConditionDefinition(name)` | Delete a CEL condition definition |\n\n## Development\n\n### Prerequisites\n\n- [Bun](https://bun.sh/) \u003e= 1.3\n- [Docker](https://www.docker.com/) (for integration and conformance tests)\n\n### Commands\n\n```bash\nbun install                            # Install dependencies\nbun run infra:setup                    # Start services + run migrations\nbun run turbo:test                     # Run all tests (infra must be running)\nbun run turbo:test:core                # Unit tests only (no infra needed)\nbun run turbo:test:conformance         # Conformance tests (infra required)\nbun run turbo:test:kysely              # Adapter tests (infra required)\nbun run turbo:test:node                # Core tests on Node.js (no infra needed)\nbun run turbo:test:deno                # Core tests on Deno (no infra needed)\nbun run build                          # Build all packages\nbun run tsc                            # Type check all packages\nbun run biome:check                    # Lint + format check (Biome)\nbun run biome:lint                     # Lint only (Biome)\nbun run biome:format                   # Auto-format (Biome)\n```\n\n### Infrastructure\n\n```bash\nbun run infra:setup           # Start services + run migrations (first time)\nbun run infra:up              # Start PostgreSQL + OpenFGA\nbun run infra:down            # Tear down with volumes (clean slate)\n```\n\nPostgreSQL and OpenFGA share the same database instance but use separate schemas\n(`tsfga` and `openfga` respectively).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femfga%2Ftsfga","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Femfga%2Ftsfga","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femfga%2Ftsfga/lists"}