diff --git a/baton/aws.mdx b/baton/aws.mdx
index cb321b2a..b0d15d50 100644
--- a/baton/aws.mdx
+++ b/baton/aws.mdx
@@ -73,6 +73,34 @@ session will be issued directly or through role chaining; C1 must derive that
topology from its configured issuance mode and apply the corresponding AWS
limit.
+## AWS STS federation token action
+
+The connector also exposes the global `issue_federation_token` action, which
+calls AWS STS `GetFederationToken` to mint temporary credentials for a named
+federated session. The credentials are returned as encrypted secret data; the
+federated user ARN and ID, the session token size, and the session token
+utilization are returned as ordinary fields.
+
+The action accepts:
+
+- `name`: the federated session name, 2 through 32 characters from
+ `[A-Za-z0-9_+=,.@-]`. It is a label for the session, not a lookup of an
+ existing IAM or Identity Center user;
+- `duration_seconds`: optional, 900 through 129,600 (AWS defaults to 43,200);
+- `policy`: an optional inline session policy, as JSON;
+- `policy_arns`: up to 10 managed policy ARNs from the calling IAM user's own
+ account;
+- `tags`: optional session tags, up to 50 key/value pairs; and
+- `minimum_session_token_size`: optional, 0 through 4,096 bytes.
+
+Two constraints are worth calling out. First, `GetFederationToken` must be
+signed with long-term IAM user keys: a connector running on assumed-role or
+other temporary credentials cannot use this action, and the calling identity
+needs `sts:GetFederationToken`. Second, the issued credentials get no
+permissions at all unless you pass `policy` or `policy_arns` — the session's
+access is the intersection of the IAM user's permissions and the policies
+supplied here.
+
## Cloud infrastructure access: Organizations and permission sets as scoped bindings
When both **Enable support for AWS Organizations** and **Enable support for AWS IAM Identity Center** are turned on, the connector also models Identity Center permission set assignments as **Cloud Infrastructure Access** bindings, alongside the existing flat per-account entitlement model. This introduces four resource types:
diff --git a/baton/azure-devops.mdx b/baton/azure-devops.mdx
index 02ff8291..d99ae4c0 100644
--- a/baton/azure-devops.mdx
+++ b/baton/azure-devops.mdx
@@ -79,6 +79,24 @@ calls the Azure DevOps Identities API and requires `vso.identity` in addition
to the `vso.auditlog` scope required by incremental sync.
+## Team administrators
+
+When **Sync teams** is enabled, each team has an **admin** entitlement that shows everyone Azure DevOps lists as a team administrator. This includes users, groups, and service principals, even if they aren't members of the team. When a group is a team administrator, its members also appear as team administrators in C1.
+
+Administrators who have access only because they are Project Administrators or Project Collection Administrators are not listed, since Azure DevOps doesn't show them as team administrators either.
+
+To read team administrators, the connector needs access to the team's security settings:
+
+- **Client secret:** no extra setup. The service principal is a Project Collection Administrator.
+- **OAuth:** add the `vso.security_manage` and `vso.identity` permissions.
+- **Personal access token:** add the **Security: Manage** and **Identity: Read** scopes.
+
+If the connector can't read the team's security settings, the sync still completes. In that case, only team administrators who are also team members are shown.
+
+
+Team administrators can't be granted or revoked from C1. Manage them in Azure DevOps. Changes to team administrators are picked up on the next full sync, not by incremental sync.
+
+
## Gather Azure DevOps credentials
Configuring the connector requires you to pass in credentials generated in Azure DevOps. Gather these credentials before you move on.
diff --git a/baton/bridge-client.mdx b/baton/bridge-client.mdx
index fcade5a9..92e7f857 100644
--- a/baton/bridge-client.mdx
+++ b/baton/bridge-client.mdx
@@ -255,6 +255,14 @@ For each scalar above, a sibling `C1_BRIDGE_SECRET_` env var accepts a `"
`ca_pem` has no scalar form (`C1_BRIDGE_CA_PEM` doesn't exist) — multi-line PEM data doesn't round-trip cleanly through shell envs, so it lives in YAML or comes from a secret backend.
+### macOS certificate trust
+
+On macOS, the bridge uses the macOS certificate trust store by default. If `SSL_CERT_FILE` or `SSL_CERT_DIR` is set, it uses the specified CA bundle or directory instead for connections that rely on system roots, including authentication, gateway, and Vault secret backend connections.
+
+The bridge's `ca_path` and `ca_pem` add certificates to these roots rather than replacing them, so `SSL_CERT_FILE` and `SSL_CERT_DIR` still affect authentication and gateway connections. For the Vault connection, `VAULT_CACERT`, `VAULT_CACERT_BYTES`, or `VAULT_CAPATH` take precedence over system roots.
+
+To use the macOS trust store, unset both variables. If other tools require them, set `GODEBUG=x509sslcertoverrideplatform=0` when running the bridge to use macOS certificate verification while leaving those variables set.
+
### Single-service env mode
For deployments that don't want to ship a YAML config at all, a single service mapping can be defined entirely in the environment. When both `C1_BRIDGE_SERVICE_LISTEN_PORT` and `C1_BRIDGE_SERVICE_BACKEND` are set, a single port is constructed from the `C1_BRIDGE_SERVICE_*` vars and **replaces** any `ports:` block loaded from YAML.
diff --git a/baton/cloudflare-zero-trust.mdx b/baton/cloudflare-zero-trust.mdx
index ad0e0d2b..b2022804 100644
--- a/baton/cloudflare-zero-trust.mdx
+++ b/baton/cloudflare-zero-trust.mdx
@@ -14,6 +14,124 @@ sidebarTitle: "Cloudflare Zero Trust"
| Access groups | | |
| Roles | | |
+## Access group rules
+
+Cloudflare Access groups grant membership using three rule lists with
+different logic:
+
+- **Include** — OR. A user matches the group if at least one rule matches.
+- **Require** — AND. A user must also match every rule in this list.
+- **Exclude** — NOT. A user must not match any rule in this list.
+
+Each list can contain several rule types (email, email domain, everyone,
+country, IP range, IdP group claim, a nested Access group, etc.). This
+connector can evaluate some of them and not others, and rules it cannot
+evaluate are **skipped** rather than treated as non-matching:
+
+| Rule type | Evaluated? | Effect on reported membership |
+| :--- | :--- | :--- |
+| `email`, `email_domain`, `everyone` | Yes | Fully enforced in all three lists. |
+| `group` (nested Access group) | In `Include` only | Reported as an expandable grant — see below. |
+| `ip`, `ip_list`, `geo`, `certificate`, `device_posture`, `auth_method`, `login_method`, `auth_context`, `external_evaluation` | No | None, by design. |
+| `email_list`, IdP group claims (`okta`, `gsuite`, `saml`, ...), service tokens | No | May over- or under-report — see below. |
+
+### Why skipped rules do not narrow membership
+
+Each list combines its rules with a boolean operator, and a skipped rule is
+treated as that operator's neutral element so it cannot change the list's
+answer: satisfied under `Require`'s AND, non-matching under `Include`'s OR
+and `Exclude`'s NOT.
+
+This matters most for `Require`. If a skipped rule were treated as "does not
+match", a single one would make the whole AND fail and the group would report
+**no members at all**. A group with `Include: email` and `Require: geo US`
+really does grant access to that user, and C1 reports it.
+
+
+**A group whose `Include` list contains nothing this connector can evaluate
+reports no members at all.** `Include` is an OR, and its neutral element is
+"no match", so a list made up entirely of skipped rules — an IdP-group claim
+on its own, for example, which is a common Access setup — produces an empty
+result.
+
+That direction under-reports rather than over-reports, and an empty group in
+an access review reads as "nobody has access" rather than "this could not be
+determined". The connector cannot tell the difference, and inventing members
+would be worse, so it logs every group in this state at sync time and lists
+the group's rules on its resource profile. Check those groups in Cloudflare
+directly.
+
+
+Contextual rules — country, IP range, mTLS certificate, device posture,
+authentication method — are never evaluated, and this is deliberate rather
+than a gap. They constrain *the request*: where it comes from and how it
+authenticated. They do not change *who* the policy grants access to, and
+Cloudflare evaluates them per request, so no connector can resolve them at
+sync time. C1 models who a policy grants access to, not the conditions under
+which that access applies.
+
+
+**Rules that name identities this connector cannot read may cause C1 to
+over-report membership.** Nested Access groups referenced from
+`Require`/`Exclude`, email lists, IdP group claims (`okta`, `gsuite`,
+`saml`) and service tokens all name real identities, but in a directory
+this connector does not sync.
+
+Because they are skipped, a user who does not satisfy a `Require` rule — or
+who should be removed by an `Exclude` rule — may still be reported as a
+member of the group. In `Include` the effect runs the other way: a member
+admitted only by such a rule is not reported at all. Every group carrying such a rule is logged at sync
+time, and its rules are listed on the group's profile in C1 under
+`include_rules` / `require_rules` / `exclude_rules`, so you can see exactly
+which conditions were not applied.
+
+For those groups, confirm membership in Cloudflare before relying on C1 for
+an access review.
+
+
+A nested Access group referenced in **Include** is represented as an
+expandable grant on the referenced group's own membership — so nested
+group membership (including further nesting) is reflected in C1 without
+this connector re-evaluating every member on every sync.
+
+
+**Nested Include membership is not reported when the same group also has
+`Require` or `Exclude` rules that this connector can evaluate.** An
+expandable grant is a union: it pulls in everyone who holds the nested
+group's membership, and the outer group's `Require`/`Exclude` rules cannot
+be applied to the members it pulls in. A member excluded by the outer group
+would otherwise still be reported as a member of it.
+
+Rather than over-report access, the connector omits the nested membership
+for those groups and reports only the members matched directly by the outer
+group's own `email`, `email_domain` and `everyone` rules. Rules that are
+skipped anyway — and a `Require` list consisting only of `everyone` — are
+not treated as restrictions, since they filter nothing. Affected groups are
+logged at sync time.
+
+
+### Granting and revoking group membership
+
+C1 adds a member to an Access group by appending an `email` rule to the
+group's `Include` list, and removes one by deleting that rule. Everything
+else about the group — its name and its `Require` and `Exclude` rules — is
+written back unchanged.
+
+Because membership is expressed as rules rather than as a member list, some
+requests cannot be carried out, and the connector refuses them rather than
+reporting a change it did not make:
+
+| Request | Result |
+| :--- | :--- |
+| Grant to someone a `Require` or `Exclude` rule keeps out | **Refused.** The rule would be written but would grant nothing, and C1 would never hold the grant, so the rule would stay in your policy unnoticed. |
+| Grant to someone already admitted by a broad rule (`everyone`, `email_domain`) | **No change.** They already have access, and adding a rule naming them would only leave a redundant one behind. |
+| Revoke from someone whose access comes from a broad rule | **Refused.** That rule governs other members too, so it cannot be edited on one person's behalf. Change it in Cloudflare instead. |
+| Revoke the last `email` rule in a group's `Include` list | **Refused.** Cloudflare rejects a group with an empty `Include` list. Delete or edit the group in Cloudflare instead. |
+| Revoke from someone who is not a member | **No change.** |
+
+Membership that comes from a nested Access group cannot be revoked here
+either: the member belongs to the referenced group, not to this one.
+
## Gather Cloudflare Zero Trust credentials
Configuring the connector requires you to pass in credentials generated in Cloudflare Zero Trust. Gather these credentials before you move on.
@@ -68,11 +186,13 @@ A user with **Super Administrator** access in Cloudflare Zero Trust must perform
- Account -> Access: Organizations, Identity Providers, and Groups -> Edit
- Account -> Access: Apps and Policies -> Read
- Account -> Access: Audit Logs -> Read
+ - Account -> Memberships -> Edit
Otherwise, set:
- Account -> Account Settings -> Read
- Account -> Access: Organizations, Identity Providers, and Groups -> Read
+ - Account -> Memberships -> Read
- Account -> Access: Apps and Policies -> Read
- Account -> Access: Audit Logs -> Read
diff --git a/baton/coupa.mdx b/baton/coupa.mdx
index d792eda9..19381edf 100644
--- a/baton/coupa.mdx
+++ b/baton/coupa.mdx
@@ -35,11 +35,25 @@ C1 can create Coupa user accounts. When you configure account provisioning for t
| Last name | Last name of the person who will own the Coupa user. |
| Email | Email address of the Coupa user. Defaults to the C1 user's primary email. |
| Login | Login for the Coupa user. Defaults to the C1 user's username. |
+| SSO identifier | Single sign-on identifier for the Coupa user. |
+| Employee number | Employee number for the Coupa user. |
+| Manager login | Login of the user's manager in Coupa. |
+| Purchasing user | Assign a Purchasing license when the account is created. |
+| Invoicing user | Assign an Invoicing license when the account is created. |
+| Sourcing user | Assign a Sourcing license when the account is created. |
+| Account security type | Numeric Coupa account security type. |
+| Authentication method | `coupa_credentials`, `ldap`, or `saml`; values are case-sensitive. |
+| Default locale | Default locale, such as `en` or `en-GB`. |
+| Default account type | Name of the user's default Coupa account type. |
+| Default currency | ISO currency code, such as `USD`. |
+| Custom fields | Map of instance-specific Coupa user field names to values. |
Every field is optional to C1. Map **First name** and **Last name** regardless — Coupa has no other source for a new user's name.
Accounts are created active. Coupa authenticates through SSO, so no password is set.
+Custom fields are sent in Coupa's `custom-fields` namespace. License assignments can also be managed after account creation through C1 License Management.
+
### Connector actions
Connector actions are custom capabilities that extend C1 automations with app-specific operations. You can use connector actions in the [Perform connector action](/product/admin/automations-steps-reference#perform-connector-action) automation step.
diff --git a/baton/databricks.mdx b/baton/databricks.mdx
index bfc4d0cc..7e61eccb 100644
--- a/baton/databricks.mdx
+++ b/baton/databricks.mdx
@@ -29,12 +29,6 @@ Provisioning **account groups** requires OAuth authentication. It is not availab
The connector authenticates with **OAuth** — an account-level service principal's client ID and secret. This is the only method currently offered.
-
-**Workspace-token (personal access token) authentication is temporarily unavailable.** It is not offered when configuring the connector, and configurations that specify it are rejected at startup. Use OAuth instead.
-
-The connector's PAT implementation is intact and the method is expected to return; it is withheld while a platform-side defect is resolved. The defect is not in this connector: a credential declared as a list of secrets is not treated as secret by the configuration layer, so a workspace token supplied through the UI would be stored unencrypted and displayed in clear text.
-
-
## Gather Databricks credentials
Configuring the connector requires you to pass in credentials generated in Databricks. Gather these credentials before you move on.
@@ -78,10 +72,6 @@ The Databricks connector authenticates with OAuth:
- OAuth client ID
- OAuth client secret
-
-Personal access token (workspace token) authentication is **temporarily unavailable** and is not offered when configuring the connector, so there is no need to generate one. See [Authentication methods](#authentication-methods) above.
-
-
Next, move on to the instructions for your chosen setup method.
## Configure the Databricks connector
@@ -214,25 +204,16 @@ stringData:
BATON_DATABRICKS_CLIENT_ID:
BATON_DATABRICKS_CLIENT_SECRET:
- # Optional: limit the sync to specific workspaces, by deployment name.
- # Mutually exclusive with BATON_DATABRICKS_EXCLUDE_WORKSPACES — set one or the other, never both.
- # BATON_WORKSPACES:
-
# Optional: exclude specific workspaces from the sync
# (workspace name, deployment name, or numeric ID).
- # Mutually exclusive with BATON_WORKSPACES — set one or the other, never both.
BATON_DATABRICKS_EXCLUDE_WORKSPACES:
# Optional: include if you want C1 to provision access using this connector
BATON_PROVISIONING: true
```
-
-**`BATON_WORKSPACES` and `BATON_DATABRICKS_EXCLUDE_WORKSPACES` cannot both be set.** They are mutually exclusive, and a config carrying both is rejected at startup before any API call. Choose one.
-
-
-OAuth requires a reachable account API. If the account API check fails at startup, the connector fails validation instead of falling back to a workspace-only sync, even when `BATON_WORKSPACES` is set.
+OAuth requires a reachable account API. If the account API check fails at startup, the connector fails validation instead of falling back to a workspace-only sync.
See the connector's README or run `--help` to see all available configuration flags and environment variables.
diff --git a/baton/google-identity-platform.mdx b/baton/google-identity-platform.mdx
index a26a9f1f..b678cf25 100644
--- a/baton/google-identity-platform.mdx
+++ b/baton/google-identity-platform.mdx
@@ -35,7 +35,7 @@ In the toolbar, click the project select dropdown, and click **NEW PROJECT**.
Create a new project for your organization:
- - **Project Name**: Choose a names, such as "C1 Integration"
+ - **Project Name**: Choose a name, such as "C1 Integration"
- **Organization/Location**: Choose the appropriate Organization/Location