ResourceSyncer interface: list resources, list entitlements, list grants.
The access graph
Your connector produces an access graph that powers access reviews, certification campaigns, provisioning workflows, and compliance reporting. This single data structure drives everything C1 does. The graph has three main node/edge types:- Resources: things that exist (users, groups, apps, roles, projects, etc.)
- Entitlements: permissions you can assign on a resource (member, admin, read, etc.)
- Grants: facts connecting principals to entitlements (Alice is a member of Engineering)
Hierarchical resources
Most access management systems flatten everything into a single list. Baton takes a different approach: resources can have parent-child relationships. This preserves the natural structure of your target systems. Consider GitHub: organizations contain repositories, repositories have branches. Or AWS: accounts contain services, services have resources. When your connector models these hierarchies, C1 can:- Show access in context (this role applies to this project, not globally)
- Enable scoped access reviews (review all access within a single org unit)
- Support inheritance patterns where they exist in the target system
parentResourceID parameter in your List() method. The SDK calls your List() first with no parent (top-level resources), then again for each parent that might have children. This inversion of control means you describe the structure; the SDK walks it.
Resource types and traits
Every resource has a resource type (string id) and can declare traits that tell C1 how to interpret it. Traits let C1 understand what kind of thing a resource is, even when different systems call it different names.TRAIT_USERTRAIT_GROUPTRAIT_ROLETRAIT_APPTRAIT_SECRETTRAIT_ROLE_SCOPE
TRAIT_USER, C1 knows it can correlate that resource with users from other systems, display it in user-centric views, and apply user-specific policies.
Entitlements
Entitlements define what can be granted. They’re attached to resources - permissions don’t float free in the system, they belong to something specific.Entitlement purpose
Entitlements have apurpose field that tells C1 how to interpret them:
Grants
Grants record who has what:The sync lifecycle
Understanding the sync lifecycle helps you write cleaner code. The SDK orchestrates everything; you implement the callbacks. The “shape” of work per resource type is:- Define type (
ResourceType) - List instances (
List) - For each resource instance, list entitlements (
Entitlements) - For each resource instance, list grants (
Grants)
nextPageToken string (empty when done).
The SDK detects a common pagination bug: returning the same next-page token you were given. This prevents infinite loops from subtle bugs.
The sync pipeline
When a connector runs, data flows through several stages. The clean separation between what you control and what C1 controls makes the system reliable and testable:- Fetch - Your connector calls the external API
- Transform - Your connector creates Resource/Entitlement/Grant objects
- Output - SDK writes objects to a .c1z file (gzip-compressed SQLite)
- Ingest - C1’s sync service reads the .c1z file
- Uplift - Raw connector records become domain objects (Apps, Resources, Grants)
ID correlation
C1 needs to know whether a resource in this sync is the same resource from a previous sync. This is where theRawId annotation matters.
When you build a resource, include its external system ID:
RawId annotation carries the external system’s identifier through the pipeline:
- Connector: Sets
RawIdannotation on the resource - Sync storage: Stored as
external_idon the connector record - Domain objects: Tracked in
source_connector_idsmap
- Correlate resources across syncs (same ID = same resource)
- Track which connector discovered which resource
- Support pre-sync reservation patterns
app.Id. For AWS, that’s the ARN. For GCP, that’s the project ID.
ID vocabulary
These terms appear throughout C1 when discussing identity correlation:
The flow is:
RawId (connector) -> external_id (sync) -> source_connector_ids or raw_baton_id (domain).
Modeling decisions
Your modeling choices expand or constrain what your organization can do with access control. Two connectors can both be “correct” and still produce very different experiences in C1:-
Entitlement granularity
- Fine-grained (read/write/admin): more precision in reviews and provisioning, more total grants and API calls.
- Coarse-grained (access/no-access): simpler, but you can’t request/revoke specific privilege levels.
-
Capability surface
- Sync-only vs sync + provision (Grant/Revoke/Create/Delete)
- Check per connector/version rather than assuming.
Guidance: Model what matters for access decisions. If you need to revoke admin access separately from read access, make them separate entitlements.
Constraints and guardrails
The SDK includes guardrails that catch common mistakes early:- Pagination invariants: your next token must progress; the SDK will detect and error on “same token” loops.
- Optional advanced behaviors exist but are not universal: targeted sync (
Get), account management, resource deletion, credential rotation. Start with the basics; add these when you need them.
Error handling
If an API call fails, return an error. The SDK handles retries for transient failures. For permanent failures (bad credentials, missing permissions), the sync stops with a clear error message.Rate limiting
Most APIs have rate limits. The SDK’s HTTP client handles backoff automatically, but be mindful of page sizes - smaller pages mean more requests.Execution modes
Connectors can run in different modes:
See Deployment for operational details on each mode.