{"id":30620509,"url":"https://github.com/aserto-dev/topaz-node","last_synced_at":"2025-08-30T13:38:12.197Z","repository":{"id":302395884,"uuid":"1009696463","full_name":"aserto-dev/topaz-node","owner":"aserto-dev","description":"Topaz TypeScript SDK","archived":false,"fork":false,"pushed_at":"2025-07-18T07:58:55.000Z","size":1230,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-07-18T11:34:30.510Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://topaz.sh","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aserto-dev.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}},"created_at":"2025-06-27T14:57:24.000Z","updated_at":"2025-07-18T07:58:35.000Z","dependencies_parsed_at":"2025-07-02T07:28:09.848Z","dependency_job_id":"a52f3629-cfac-469c-b9c4-0a1cb3128fcc","html_url":"https://github.com/aserto-dev/topaz-node","commit_stats":null,"previous_names":["aserto-dev/topaz-node"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/aserto-dev/topaz-node","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aserto-dev%2Ftopaz-node","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aserto-dev%2Ftopaz-node/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aserto-dev%2Ftopaz-node/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aserto-dev%2Ftopaz-node/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aserto-dev","download_url":"https://codeload.github.com/aserto-dev/topaz-node/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aserto-dev%2Ftopaz-node/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272858486,"owners_count":25005092,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-30T02:00:09.474Z","response_time":77,"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":"2025-08-30T13:38:11.542Z","updated_at":"2025-08-30T13:38:12.188Z","avatar_url":"https://github.com/aserto-dev.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# topaz-node\n\nA Node.js client for interacting with the Topaz Directory and Authorizer APIs.\n\n---\n\n## Table of Contents\n\n- [Directory](#directory)\n  - [Directory Client](#directory-client)\n  - [Getting Objects and Relations](#getting-objects-and-relations)\n  - [Setting Objects and Relations](#setting-objects-and-relations)\n  - [Checking Permissions and Relations](#checking-permissions-and-relations)\n  - [Manifest](#manifest)\n  - [Import](#import)\n  - [Export](#export)\n  - [Custom Headers](#custom-headers)\n  - [Serializing Data](#serializing-data)\n- [Authorizer](#authorizer)\n  - [Authorizer Client](#authorizer-client)\n  - [Methods](#methods)\n  - [Custom Headers](#custom-headers-1)\n\n---\n\n## Directory\n\nThe Directory APIs can be used to get, set, or delete object instances, relation instances, and manifests. They can also be used to check whether a user has a permission or relation on an object instance.\n\n### Directory Client\n\n#### Configuration Types\n\n```typescript\ntype ServiceConfig = {\n  url?: string;\n  tenantId?: string;\n  apiKey?: string;\n  caFile?: string;\n  insecure?: boolean;\n};\n\nexport type DirectoryConfig = ServiceConfig \u0026 {\n  reader?: ServiceConfig;\n  writer?: ServiceConfig;\n  importer?: ServiceConfig;\n  exporter?: ServiceConfig;\n  model?: ServiceConfig;\n};\n```\n\n#### Initialization\n\nYou can initialize a directory client as follows:\n\n```typescript\nimport { Directory } from \"topaz-node\";\n\nconst directoryClient = new Directory({\n  url: 'localhost:9292',\n  caFile: `${process.env.HOME}/.local/share/topaz/certs/grpc-ca.crt`\n});\n```\n\n**Parameters:**\n\n- `url`: Hostname:port of directory service (**required**)\n- `apiKey`: API key for directory service (**required** if using hosted directory)\n- `tenantId`: Aserto tenant ID (**required** if using hosted directory)\n- `caFile`: Path to the directory CA file (optional)\n- `insecure`: Skip server certificate and domain verification (optional, defaults to `false`)\n- `reader`: ServiceConfig for the reader client (optional)\n- `writer`: ServiceConfig for the writer client (optional)\n- `importer`: ServiceConfig for the importer client (optional)\n- `exporter`: ServiceConfig for the exporter client (optional)\n- `model`: ServiceConfig for the model client (optional)\n\n#### Example: Custom Writer Client\n\nDefine a writer client that uses the same credentials but connects to `localhost:9393`. All other services will have the default configuration:\n\n```typescript\nimport { Directory } from \"topaz-node\";\n\nconst directoryClient = new Directory({\n  url: 'localhost:9292',\n  tenantId: '1234',\n  apiKey: 'my-api-key',\n  writer: {\n    url: 'localhost:9393'\n  }\n});\n```\n\n### Getting Objects and Relations\n\n#### `object` function\n\nGet an object instance with the specified type and id.\n\n```typescript\nconst user = await directoryClient.object({ objectType: 'user', objectId: 'euang@acmecorp.com' });\n```\n\nHandle a specific Directory Error:\n\n```typescript\nimport { NotFoundError } from  \"topaz-node\";\n\ntry {\n  await directoryClient.object({\n    objectType: \"user\",\n    objectId: \"euang@acmecorp.com\",\n  });\n} catch (error) {\n  if (error instanceof NotFoundError) {\n    // handle the case where the object was not found\n  }\n  throw error;\n}\n```\n\n#### `relation` function\n\nGet a relation of a certain type between a subject and an object.\n\n```typescript\nconst identity = 'euang@acmecorp.com';\nconst relation = await directoryClient.relation({\n  subjectType: 'user',\n  subjectId: 'euang@acmecorp.com',\n  relation: 'identifier',\n  objectType: 'identity',\n  objectId: identity\n});\n```\n\n#### `relations` function\n\nGet all relations of a certain type for a given object.\n\n```typescript\nconst relations = await directoryClient.relations({\n  subjectType: 'subject-type',\n  relation: 'relation-name',\n  objectType: 'object-type',\n  objectId: 'object-id',\n});\n```\n\n### Setting Objects and Relations\n\n#### `setObject` function\n\nCreate an object instance with the specified fields.\n\n```typescript\nconst user = await directoryClient.setObject({\n  object: {\n    type: \"user\",\n    id: \"test-object\",\n    properties: {\n      displayName: \"test object\"\n    }\n  }\n});\n```\n\n#### `setRelation` function\n\nCreate a relation with a specified name between two objects.\n\n```typescript\nconst relation = await directoryClient.setRelation({\n  subjectId: 'subjectId',\n  subjectType: 'subjectType',\n  relation: 'relationName',\n  objectType: 'objectType',\n  objectId: 'objectId',\n});\n```\n\n#### `deleteObject` function\n\nDelete an object instance with the specified type and key.\n\n```typescript\nawait directoryClient.deleteObject({ objectType: 'user', objectId: 'euang@acmecorp.com' });\n```\n\n#### `deleteRelation` function\n\nDelete a relation between two objects.\n\n```typescript\nawait directoryClient.deleteRelation({\n  subjectType: 'subjectType',\n  subjectId: 'subjectId',\n  relation: 'relationName',\n  objectType: 'objectType',\n  objectId: 'objectId',\n});\n```\n\n### Checking Permissions and Relations\n\nYou can evaluate graph queries over the directory to determine whether a subject (e.g., user) has a permission or a relation to an object instance.\n\n#### `check` function\n\nCheck that a `user` object with the key `euang@acmecorp.com` has the `read` permission in the `admin` group:\n\n```typescript\nconst check = await directoryClient.check({\n  subjectId: 'euang@acmecorp.com',\n  subjectType: 'user',\n  relation: 'read',\n  objectType: 'group',\n  objectId: 'admin',\n});\n```\n\n### Example\n\n```typescript\nconst identity = 'euang@acmecorp.com';\nconst relation = await directoryClient.relation(\n  {\n    subjectType: 'user',\n    objectType: 'identity',\n    objectId: identity,\n    relation: 'identifier',\n    subjectId: 'euang@acmecorp.com'\n  }\n);\n\nif (!relation) {\n  throw new Error(`No relations found for identity ${identity}`)\n};\n\nconst user = await directoryClient.object(\n  { objectId: relation.subjectId, objectType: relation.subjectType }\n);\n```\n\n### Manifest\n\nYou can get, set, or delete the manifest\n\n#### `getManifest` function\n\n```typescript\nawait directoryClient.getManifest();\n```\n\n#### `setManifest` function\n\n```typescript\nawait directoryClient.setManifest(`\n# yaml-language-server: $schema=https://www.topaz.sh/schema/manifest.json\n---\n### model ###\nmodel:\n  version: 3\n\n### object type definitions ###\ntypes:\n  ### display_name: User ###\n  user:\n    relations:\n      ### display_name: user#manager ###\n      manager: user\n\n  ### display_name: Identity ###\n  identity:\n    relations:\n      ### display_name: identity#identifier ###\n      identifier: user\n\n  ### display_name: Group ###\n  group:\n    relations:\n      ### display_name: group#member ###\n      member: user\n    permissions:\n      read: member\n`);\n```\n\n#### `deleteManifest` function\n\n```typescript\nawait directoryClient.deleteManifest();\n```\n\n### Import\n\n```typescript\nimport { ImportMsgCase, ImportOpCode, createImportRequest } from \"topaz-node\"\nconst importRequest = createImportRequest([\n  {\n    opCode: ImportOpCode.SET,\n    msg: {\n      case: ImportMsgCase.OBJECT,\n      value: {\n        id: \"import-user\",\n        type: \"user\",\n        properties: { foo: \"bar\" },\n        displayName: \"name1\",\n      },\n    },\n  },\n  {\n    opCode: ImportOpCode.SET,\n    msg: {\n      case: ImportMsgCase.OBJECT,\n      value: {\n        id: \"import-group\",\n        type: \"group\",\n        properties: {},\n        displayName: \"name2\",\n      },\n    },\n  },\n  {\n    opCode: ImportOpCode.SET,\n    msg: {\n      case: ImportMsgCase.RELATION,\n      value: {\n        subjectId: \"import-user\",\n        subjectType: \"user\",\n        objectId: \"import-group\",\n        objectType: \"group\",\n        relation: \"member\",\n      },\n    },\n  },\n]);\n\nconst resp = await directoryClient.import(importRequest);\nawait (readAsyncIterable(resp))\n```\n\n### Export\n\n```typescript\nconst response = await readAsyncIterable(\n  await directoryClient.export({ options: \"DATA\" })\n)\n\n```\n\n\n### Custom Headers\n\n```typescript\n// passing custom headers to a request\nconst user = await directoryClient.object(\n  {\n    objectType: \"user\",\n    objectId: \"euang@acmecorp.com\",\n  },\n  {\n    headers: {\n      customKey: \"customValue\",\n    },\n  }\n);\n```\n\n### Serializing data\n\nUse [Protocol Buffers](https://github.com/bufbuild/protobuf-es) to serialize data.\n\n\n```typescript\nimport { GetObjectsResponseSchema, toJson } from \"topaz-node\";\n\nconst objects = await directoryClient.objects({objectType: \"user\"});\nconst json = toJson(GetObjectsResponseSchema, objects)\n```\n\n\n## Authorizer\n\n### Authorizer Client\n```typescript\ninterface Authorizer {\n  config: AuthorizerConfig,\n};\n\ntype AuthorizerConfig = {\n  authorizerServiceUrl?: string;\n  tenantId?: string;\n  authorizerApiKey?: string;\n  token?: string;\n  caFile?: string;\n  insecure?: boolean;\n};\n```\n```typescript\nconst authClient = new Authorizer({\n  authorizerServiceUrl: \"localhost:8282\",\n  caFile: `${process.env.HOME}/.local/share/topaz/certs/grpc-ca.crt`\n});\n```\n\n- `authorizerServiceUrl`: Hostname:port of authorizer service (**required**)\n- `authorizerApiKey`: API key for authorizer service (**required** if using hosted authorizer)\n- `tenantId`: Aserto tenant ID (**required** if using hosted authorizer)\n- `caFile`: Path to the authorizer CA file (optional)\n- `insecure`: Skip server certificate and domain verification (optional, defaults to `false`)\n\n#### Example:\n```typescript\nimport {\n  Authorizer,\n  identityContext,\n  policyContext,\n  policyInstance,\n} from \"topaz-node\";\n\nconst authClient = new Authorizer(\n  {\n    authorizerServiceUrl: \"localhost:8282\",\n    caFile: `${process.env.HOME}/.local/share/topaz/certs/grpc-ca.crt`\n  },\n);\n\nauthClient\n  .Is({\n    identityContext: {\n      identity: \"rick@the-citadel.com\",\n      type: IdentityType.SUB,\n    },\n    policyInstance: {\n      name: \"rebac\",\n    },\n    policyContext: {\n      path: \"rebac.check\",\n      decisions: [\"allowed\"],\n    },\n    resourceContext: {\n      object_type: \"group\",\n      object_id: \"evil_genius\",\n      relation: \"member\",\n    },\n  })\n```\n\n### Methods\n```typescript\n// Is\n// (method) Authorizer.Is(params: IsRequest, options?: CallOptions): Promise\u003cIsResponse\u003e\nawait authClient\n  .Is({\n    identityContext: {\n      identity: \"rick@the-citadel.com\",\n      type: IdentityType.SUB,\n    },\n    policyInstance: {\n      name: \"todo\",\n    },\n    policyContext: {\n      path: \"todoApp.POST.todos\",\n      decisions: [\"allowed\"],\n    },\n    resourceContext: {\n      ownerID: \"fd1614d3-c39a-4781-b7bd-8b96f5a5100d\",\n    },\n  })\n\n// Query\n// (method) Authorizer.Query(params: QueryRequest, options?: CallOptions): Promise\u003cJsonObject\u003e\nawait authClient\n  .Query({\n    identityContext: {\n      identity: \"rick@the-citadel.com\",\n      type: IdentityType.SUB,\n    },\n    policyInstance: {\n      name: \"todo\",\n    },\n    policyContext: {\n      path: \"todoApp.POST.todos\",\n      decisions: [\"allowed\"],\n    },\n    resourceContext: {\n      ownerID: \"fd1614d3-c39a-4781-b7bd-8b96f5a5100d\",\n    },\n    query: \"x = data\",\n  })\n\n\n// DecisionTree\n// (method) Authorizer.DecisionTree(params: DecisionTreeRequest, options?: CallOptions): Promise\u003c{\n//     path: Path;\n//     pathRoot: string;\n// }\u003e\nawait authClient\n  .DecisionTree({\n    identityContext: {\n      identity: \"rick@the-citadel.com\",\n      type: IdentityType.SUB,\n    },\n    policyInstance: {\n      name: \"todo\",\n    },\n    policyContext: {\n      path: \"todoApp.POST.todos\",\n      decisions: [\"allowed\"],\n    },\n    resourceContext: {\n      ownerID: \"fd1614d3-c39a-4781-b7bd-8b96f5a5100d\",\n    },\n  })\n\n\n// ListPolicies\n// (method) Authorizer.ListPolicies(params: PlainMessage\u003cListPoliciesRequest\u003e, options?: CallOptions): Promise\u003cModule[]\u003e\nawait authClient.ListPolicies({ policyInstance: { name: \"todo\" } })\n```\n\n#### Custom Headers\n```typescript\nawait authClient.ListPolicies(\n  { policyInstance: { name: \"todo\" } }\n  { headers: { customKey: \"customValue\" } }\n);\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faserto-dev%2Ftopaz-node","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faserto-dev%2Ftopaz-node","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faserto-dev%2Ftopaz-node/lists"}