{
  "schemaVersion": "1.0",
  "title": "Configure Cal.diy authentication and tenant routing",
  "description": "Use this procedure to give one aip-host-cal-diy instance access to one Cal.diy account and make that account reachable only through its admitted AIP tenant binding. The provider secret stays in the host deployment; an AIP action cannot sele",
  "canonical": "https://getaip.org/docs/connectors/cal-diy/getting-started/authentication-and-tenant-routing",
  "route": "/docs/connectors/cal-diy/getting-started/authentication-and-tenant-routing",
  "source": "docs/connectors/cal-diy/getting-started/authentication-and-tenant-routing.md",
  "protocol": "Agent Interoperability Protocol",
  "protocolVersion": "1.0",
  "section": "Connectors",
  "documentType": "Connector",
  "language": "en",
  "downloads": {
    "md": "/docs/download/connectors/cal-diy/getting-started/authentication-and-tenant-routing.md",
    "txt": "/docs/download/connectors/cal-diy/getting-started/authentication-and-tenant-routing.txt",
    "json": "/docs/download/connectors/cal-diy/getting-started/authentication-and-tenant-routing.json",
    "pdf": "/docs/download/connectors/cal-diy/getting-started/authentication-and-tenant-routing.pdf"
  },
  "content": {
    "format": "text/markdown",
    "markdown": "---\ntitle: Configure Cal.diy authentication and tenant routing\ndescription: Bind one standalone host to one tenant and Cal.diy account without exposing provider secrets\nkind: how-to\naudience: operator\nappliesTo: \"1.x\"\nwritingStandard: \"aip-docs/1.0\"\nlastReviewedRevision: \"97be86e9efedf07ecf1783b03800f683f107fb04\"\nconnector: cal-diy\n---\n\n# Configure Cal.diy authentication and tenant routing\n\nUse this procedure to give one `aip-host-cal-diy` instance access to one\nCal.diy account and make that account reachable only through its admitted AIP\ntenant binding. The provider secret stays in the host deployment; an AIP action\ncannot select or supply it.\n\nThe reviewed standalone host uses a one-tenant, one-account model. Deploy a\nseparate admitted instance when another tenant or Cal.diy account needs a\ndifferent ownership boundary.\n\n## Understand the three trust layers\n\nDo not treat these identities as one credential:\n\n| Layer | Establishes | Does not contain |\n|---|---|---|\n| AIP caller authentication | Which principal reached product-neutral `aipd` | Cal.diy token or client secret |\n| Fleet and host identity | Which admitted tenant, account, instance, replica, and gateway may use the host | Raw provider secret |\n| Cal.diy provider authentication | Which Cal.diy account authorizes the fixed provider request | Caller-selectable tenant or account |\n\nThe central gateway authenticates the caller and chooses an eligible instance\nfrom the registry. The connector host accepts messages only from its configured\ngateway principal and signing identity. It then replaces identity assertions\nfrom the action payload with its own verified tenant membership, external\naccount reference, and optional non-secret credential metadata.\n\nFinally, the Cal.diy client reads the provider credential chosen by the host\noperator. Changing `identity`, `tenant`, `account_id`, or credential-looking\nfields in action input does not change that client credential.\n\n## 1. Choose the provider authentication mode\n\nThe standalone host accepts exactly one of two modes.\n\n| Mode | Configure | Provider headers | Appropriate source material |\n|---|---|---|---|\n| Bearer | Token file | `Authorization: Bearer …` | API key, managed-user token, or OAuth access token |\n| Cal platform client | Public client id and client-secret file | `x-cal-client-id` and `x-cal-secret-key` | Cal platform client credentials |\n\nThe client-header mode is not an OAuth token exchange. The connector sends the\ntwo configured headers directly. It does not open a browser flow, call the\nprovider token endpoint, or refresh an OAuth credential.\n\nChoose the smallest provider identity and permission set that covers the\nadmitted capability families. Do not use a platform administrator credential\nmerely because the connector's frozen catalogue prevents arbitrary routes.\n\n## 2. Provision the provider secret file\n\nCreate the secret through the deployment's secret manager and mount it as an\nowner-controlled file. The host requires a regular, non-symlink file. On Unix,\ngroup or world permissions cause startup to fail.\n\nFor a pre-provisioned mount point, the expected permission shape is:\n\n```text\n-rw------- 1 <host-uid> <host-gid> ... /run/secrets/cal-diy-provider\n```\n\nThe file must contain one non-empty UTF-8 value after surrounding whitespace\nis trimmed. The Cal.diy host reads at most 16 KiB. The bootstrap reader keeps\nthe temporary string in zeroizing memory, and the connector redacts secret\nmaterial from its debug representation and errors.\n\nDo not place the secret in:\n\n- an action payload, manifest, admission package, or registry record;\n- a command-line argument or ordinary environment variable;\n- an image layer, Compose file, example, log, or retained qualification\n  artifact;\n- a provider base URL as user information.\n\n## 3. Configure the Cal.diy client boundary\n\nSet a fixed provider origin and stable account id for the instance.\n\nFor Bearer authentication:\n\n```text\nAIPD_CAL_DIY_BASE_URL=https://cal.example.test\nAIPD_CAL_DIY_ACCOUNT_ID=calendar_primary\nAIPD_CAL_DIY_BEARER_TOKEN_FILE=/run/secrets/cal-diy-provider\n```\n\nFor Cal platform client headers:\n\n```text\nAIPD_CAL_DIY_BASE_URL=https://cal.example.test\nAIPD_CAL_DIY_ACCOUNT_ID=calendar_primary\nAIPD_CAL_DIY_OAUTH_CLIENT_ID=client_public_id\nAIPD_CAL_DIY_OAUTH_CLIENT_SECRET_FILE=/run/secrets/cal-diy-provider\n```\n\nDo not set the Bearer file with either client setting. The client id and secret\nfile are a required pair. A partial pair, an empty client id, or both modes\ncauses startup to fail.\n\nThe account id partitions connector state. It must contain 1 to 128 ASCII\nletters, digits, dots, underscores, or hyphens. Treat it as immutable ownership\nmetadata for the instance; do not derive it from action input.\n\nThe provider URL must:\n\n- use HTTPS, except for an explicit loopback development origin;\n- contain an authority and no embedded username or password;\n- identify the fixed Cal.diy deployment rather than an action-selected host.\n\nThe client removes a configured query and fragment, refuses non-loopback plain\nHTTP, does not follow redirects with credentials, and applies a 30-second\nrequest timeout. These checks do not replace deployment DNS, egress, or TLS\npolicy.\n\n## 4. Bind the host to one tenant and external account\n\nConfigure the fleet identity separately from the provider secret:\n\n```text\nAIP_CONNECTOR_HOST_TENANT_ID=tenant-calendar-production\nAIP_CONNECTOR_HOST_MEMBERSHIP_ID=membership-cal-primary\nAIP_CONNECTOR_HOST_EXTERNAL_ACCOUNT_ID=calendar_primary\nAIP_CONNECTOR_HOST_EXTERNAL_ACCOUNT_SYSTEM=cal_diy\n```\n\nThe external account id and system must be present together or both absent.\nFor a Cal.diy production instance, set both and make the external account id\nmatch `AIPD_CAL_DIY_ACCOUNT_ID` by deployment policy.\n\nThe reviewed code validates each field independently; it does not promise an\nautomatic equality check between those two account-id settings. Verify the\nequality in the admission material and deployment configuration before\nenabling the tenant binding.\n\nThe registry binding must associate only the intended tenant and admitted\nCal.diy capabilities with this instance. The host's verified membership then\nreasserts the same tenant on every invocation. An action cannot redirect the\nrequest to another instance merely by naming another tenant or account.\n\n## 5. Configure credential identity and revision metadata\n\nThe generic host can attach a non-secret credential handle to its verified\nexecution context. Configure its four required parts together:\n\n```text\nAIP_CONNECTOR_HOST_CREDENTIAL_ID=cal-primary-provider\nAIP_CONNECTOR_HOST_CREDENTIAL_ISSUER=cal_diy_deployment\nAIP_CONNECTOR_HOST_CREDENTIAL_SCOPES=cal_diy:profile.get\nAIP_CONNECTOR_HOST_CREDENTIAL_REVISION=cal-primary-2026-07-26-01\nAIP_CONNECTOR_HOST_CREDENTIAL_POLICY_FILE=/run/config/cal-credential-policy.json\nAIP_CONNECTOR_HOST_SECRET_PROVIDER_REF=secret://production/cal-diy/primary\n```\n\nUse the exact operation scopes granted to the admitted instance. Add scopes as\na comma-delimited list; do not copy a wildcard from a qualification fixture\ninto production.\n\nThe handle id, issuer, scopes, revision, and secret-provider reference are\nidentity and revocation metadata. They are not the raw provider secret and do\nnot cause the reviewed standalone Cal.diy binary to resolve a different\ncredential for each action. The actual client credential still comes from the\nsingle Cal.diy secret file read at host startup.\n\nThe reloadable credential-revision policy can deny a revoked revision before a\nprovider side effect. A missing, unreadable, invalid, or non-authorizing policy\nfails closed. Use it with registry and deployment rotation; do not mistake it\nfor hot reload of the provider secret itself.\n\n## 6. Verify routing before enabling mutations\n\nKeep the new tenant capability binding disabled during admission and host\nstartup. Verify:\n\n1. the host registers with the expected connector type, version, instance,\n   replica, artifact digest, tenant, external account, and credential revision;\n2. the host readiness probe succeeds against the configured profile endpoint;\n3. discovery for the intended tenant contains the expected Cal.diy read\n   capability and no binding for another tenant;\n4. `cap:cal_diy:profile.get` completes through the normal signed gateway path;\n5. the provider request is associated with the expected Cal.diy account;\n6. an unauthorized tenant has no eligible route rather than falling back to\n   this instance.\n\nUse the [Cal.diy quickstart](quickstart.md) for the deterministic read flow.\nFollow the production fleet deployment guide for the disabled-binding,\nadmission, enablement, and rollback sequence. Do not start verification with a\nbooking mutation.\n\n## 7. Rotate a provider credential\n\nThe Cal.diy secret is read once when a host process starts. Replacing the file\nunder a running process is not documented as a credential reload.\n\nRotate through a bounded replica rollout:\n\n1. create a new provider credential with the required Cal.diy permissions;\n2. mount it at a new owner-controlled secret path without changing the old\n   replica;\n3. assign a new credential revision and configuration revision;\n4. update the revision policy and admission or route material through its\n   reviewed control path;\n5. start a replacement replica with the new secret file and verify a read;\n6. route new assignments to the replacement and drain the old replica;\n7. revoke the old provider credential only after in-flight work and rollback\n   requirements are resolved;\n8. retain the identities, timestamps, verification, and revocation evidence.\n\nDo not rotate by editing an action, reusing a replica identity for two active\nprocesses, or changing the provider account behind an unchanged instance.\n\n## Library-level tenant credential routing\n\nThe Cal.diy connector library contains an optional\n`with_tenant_credential_routing` composition. It maps a verified runtime tenant\nto an account and resolves a tenant-bound opaque Bearer handle with the exact\n`cal_diy:<operation>` scope. Missing membership, wrong issuer, wrong tenant,\nexpired handle, missing scope, or resolution failure is denied.\n\nThe reviewed `aip-host-cal-diy` binary does not enable that library mode. Its\npublic model is one account and one provider secret per admitted instance.\nDo not document the library feature as a standalone-host setting, and do not\ndeploy the migration-only bundled daemon as the target fleet architecture.\n\n## Resolve authentication and routing failures\n\n| Symptom | Decision |\n|---|---|\n| Host requires one Bearer file or one complete client pair | Remove the partial or conflicting auth mode; never weaken the startup check |\n| Secret file is rejected | Check regular-file type, ownership, mode, UTF-8 content, and 16 KiB bound without printing the secret |\n| Provider URL is rejected | Use HTTPS with an authority and no embedded credentials; plain HTTP is loopback-only |\n| Account id is rejected | Use the documented 1-to-128-character safe identifier and keep it stable |\n| External account configuration is rejected | Set id and system together, then compare the id with the Cal.diy account setting |\n| Credential handle configuration is rejected | Provide id, issuer, non-empty scopes, and revision together |\n| Host registers but tenant discovery is empty | Compare the admitted binding, tenant, instance, capability, and route policy before touching the provider secret |\n| Provider returns `401` or `403` | Verify credential type, account, expiry, and permissions; do not retry a mutation with a different secret |\n| Provider returns a redirect | Correct the fixed base URL or upstream configuration; the client deliberately does not follow it with credentials |\n\n## Related documentation\n\n- [Cal.diy connector overview](../README.md)\n- [Cal.diy quickstart](quickstart.md)\n- [Rotate connector credentials](../../../guides/connector-credentials-and-rotation.md)\n- [Connector registry and routing](../../../architecture/connector-registry-and-routing.md)\n- [Security model](../../../architecture/security-model.md)\n",
    "text": "Configure Cal.diy authentication and tenant routing\n\nUse this procedure to give one aip-host-cal-diy instance access to one\nCal.diy account and make that account reachable only through its admitted AIP\ntenant binding. The provider secret stays in the host deployment; an AIP action\ncannot select or supply it.\n\nThe reviewed standalone host uses a one-tenant, one-account model. Deploy a\nseparate admitted instance when another tenant or Cal.diy account needs a\ndifferent ownership boundary.\n\nUnderstand the three trust layers\n\nDo not treat these identities as one credential:\n\n| Layer | Establishes | Does not contain |\n\n| AIP caller authentication | Which principal reached product-neutral aipd | Cal.diy token or client secret |\n| Fleet and host identity | Which admitted tenant, account, instance, replica, and gateway may use the host | Raw provider secret |\n| Cal.diy provider authentication | Which Cal.diy account authorizes the fixed provider request | Caller-selectable tenant or account |\n\nThe central gateway authenticates the caller and chooses an eligible instance\nfrom the registry. The connector host accepts messages only from its configured\ngateway principal and signing identity. It then replaces identity assertions\nfrom the action payload with its own verified tenant membership, external\naccount reference, and optional non-secret credential metadata.\n\nFinally, the Cal.diy client reads the provider credential chosen by the host\noperator. Changing identity, tenant, accountid, or credential-looking\nfields in action input does not change that client credential.\n1. Choose the provider authentication mode\n\nThe standalone host accepts exactly one of two modes.\n\n| Mode | Configure | Provider headers | Appropriate source material |\n\n| Bearer | Token file | Authorization: Bearer … | API key, managed-user token, or OAuth access token |\n| Cal platform client | Public client id and client-secret file | x-cal-client-id and x-cal-secret-key | Cal platform client credentials |\n\nThe client-header mode is not an OAuth token exchange. The connector sends the\ntwo configured headers directly. It does not open a browser flow, call the\nprovider token endpoint, or refresh an OAuth credential.\n\nChoose the smallest provider identity and permission set that covers the\nadmitted capability families. Do not use a platform administrator credential\nmerely because the connector's frozen catalogue prevents arbitrary routes.\n2. Provision the provider secret file\n\nCreate the secret through the deployment's secret manager and mount it as an\nowner-controlled file. The host requires a regular, non-symlink file. On Unix,\ngroup or world permissions cause startup to fail.\n\nFor a pre-provisioned mount point, the expected permission shape is:\n\n-rw------- 1   ... /run/secrets/cal-diy-provider\n\nThe file must contain one non-empty UTF-8 value after surrounding whitespace\nis trimmed. The Cal.diy host reads at most 16 KiB. The bootstrap reader keeps\nthe temporary string in zeroizing memory, and the connector redacts secret\nmaterial from its debug representation and errors.\n\nDo not place the secret in:\n• an action payload, manifest, admission package, or registry record;\n• a command-line argument or ordinary environment variable;\n• an image layer, Compose file, example, log, or retained qualification\n  artifact;\n• a provider base URL as user information.\n3. Configure the Cal.diy client boundary\n\nSet a fixed provider origin and stable account id for the instance.\n\nFor Bearer authentication:\n\nAIPDCALDIYBASEURL=https://cal.example.test\nAIPDCALDIYACCOUNTID=calendarprimary\nAIPDCALDIYBEARERTOKENFILE=/run/secrets/cal-diy-provider\n\nFor Cal platform client headers:\n\nAIPDCALDIYBASEURL=https://cal.example.test\nAIPDCALDIYACCOUNTID=calendarprimary\nAIPDCALDIYOAUTHCLIENTID=clientpublicid\nAIPDCALDIYOAUTHCLIENTSECRETFILE=/run/secrets/cal-diy-provider\n\nDo not set the Bearer file with either client setting. The client id and secret\nfile are a required pair. A partial pair, an empty client id, or both modes\ncauses startup to fail.\n\nThe account id partitions connector state. It must contain 1 to 128 ASCII\nletters, digits, dots, underscores, or hyphens. Treat it as immutable ownership\nmetadata for the instance; do not derive it from action input.\n\nThe provider URL must:\n• use HTTPS, except for an explicit loopback development origin;\n• contain an authority and no embedded username or password;\n• identify the fixed Cal.diy deployment rather than an action-selected host.\n\nThe client removes a configured query and fragment, refuses non-loopback plain\nHTTP, does not follow redirects with credentials, and applies a 30-second\nrequest timeout. These checks do not replace deployment DNS, egress, or TLS\npolicy.\n4. Bind the host to one tenant and external account\n\nConfigure the fleet identity separately from the provider secret:\n\nAIPCONNECTORHOSTTENANTID=tenant-calendar-production\nAIPCONNECTORHOSTMEMBERSHIPID=membership-cal-primary\nAIPCONNECTORHOSTEXTERNALACCOUNTID=calendarprimary\nAIPCONNECTORHOSTEXTERNALACCOUNTSYSTEM=caldiy\n\nThe external account id and system must be present together or both absent.\nFor a Cal.diy production instance, set both and make the external account id\nmatch AIPDCALDIYACCOUNTID by deployment policy.\n\nThe reviewed code validates each field independently; it does not promise an\nautomatic equality check between those two account-id settings. Verify the\nequality in the admission material and deployment configuration before\nenabling the tenant binding.\n\nThe registry binding must associate only the intended tenant and admitted\nCal.diy capabilities with this instance. The host's verified membership then\nreasserts the same tenant on every invocation. An action cannot redirect the\nrequest to another instance merely by naming another tenant or account.\n5. Configure credential identity and revision metadata\n\nThe generic host can attach a non-secret credential handle to its verified\nexecution context. Configure its four required parts together:\n\nAIPCONNECTORHOSTCREDENTIALID=cal-primary-provider\nAIPCONNECTORHOSTCREDENTIALISSUER=caldiydeployment\nAIPCONNECTORHOSTCREDENTIALSCOPES=caldiy:profile.get\nAIPCONNECTORHOSTCREDENTIALREVISION=cal-primary-2026-07-26-01\nAIPCONNECTORHOSTCREDENTIALPOLICYFILE=/run/config/cal-credential-policy.json\nAIPCONNECTORHOSTSECRETPROVIDERREF=secret://production/cal-diy/primary\n\nUse the exact operation scopes granted to the admitted instance. Add scopes as\na comma-delimited list; do not copy a wildcard from a qualification fixture\ninto production.\n\nThe handle id, issuer, scopes, revision, and secret-provider reference are\nidentity and revocation metadata. They are not the raw provider secret and do\nnot cause the reviewed standalone Cal.diy binary to resolve a different\ncredential for each action. The actual client credential still comes from the\nsingle Cal.diy secret file read at host startup.\n\nThe reloadable credential-revision policy can deny a revoked revision before a\nprovider side effect. A missing, unreadable, invalid, or non-authorizing policy\nfails closed. Use it with registry and deployment rotation; do not mistake it\nfor hot reload of the provider secret itself.\n6. Verify routing before enabling mutations\n\nKeep the new tenant capability binding disabled during admission and host\nstartup. Verify:\n1. the host registers with the expected connector type, version, instance,\n   replica, artifact digest, tenant, external account, and credential revision;\n2. the host readiness probe succeeds against the configured profile endpoint;\n3. discovery for the intended tenant contains the expected Cal.diy read\n   capability and no binding for another tenant;\n4. cap:caldiy:profile.get completes through the normal signed gateway path;\n5. the provider request is associated with the expected Cal.diy account;\n6. an unauthorized tenant has no eligible route rather than falling back to\n   this instance.\n\nUse the Cal.diy quickstart (quickstart.md) for the deterministic read flow.\nFollow the production fleet deployment guide for the disabled-binding,\nadmission, enablement, and rollback sequence. Do not start verification with a\nbooking mutation.\n7. Rotate a provider credential\n\nThe Cal.diy secret is read once when a host process starts. Replacing the file\nunder a running process is not documented as a credential reload.\n\nRotate through a bounded replica rollout:\n1. create a new provider credential with the required Cal.diy permissions;\n2. mount it at a new owner-controlled secret path without changing the old\n   replica;\n3. assign a new credential revision and configuration revision;\n4. update the revision policy and admission or route material through its\n   reviewed control path;\n5. start a replacement replica with the new secret file and verify a read;\n6. route new assignments to the replacement and drain the old replica;\n7. revoke the old provider credential only after in-flight work and rollback\n   requirements are resolved;\n8. retain the identities, timestamps, verification, and revocation evidence.\n\nDo not rotate by editing an action, reusing a replica identity for two active\nprocesses, or changing the provider account behind an unchanged instance.\n\nLibrary-level tenant credential routing\n\nThe Cal.diy connector library contains an optional\nwithtenantcredentialrouting composition. It maps a verified runtime tenant\nto an account and resolves a tenant-bound opaque Bearer handle with the exact\ncaldiy: scope. Missing membership, wrong issuer, wrong tenant,\nexpired handle, missing scope, or resolution failure is denied.\n\nThe reviewed aip-host-cal-diy binary does not enable that library mode. Its\npublic model is one account and one provider secret per admitted instance.\nDo not document the library feature as a standalone-host setting, and do not\ndeploy the migration-only bundled daemon as the target fleet architecture.\n\nResolve authentication and routing failures\n\n| Symptom | Decision |\n\n| Host requires one Bearer file or one complete client pair | Remove the partial or conflicting auth mode; never weaken the startup check |\n| Secret file is rejected | Check regular-file type, ownership, mode, UTF-8 content, and 16 KiB bound without printing the secret |\n| Provider URL is rejected | Use HTTPS with an authority and no embedded credentials; plain HTTP is loopback-only |\n| Account id is rejected | Use the documented 1-to-128-character safe identifier and keep it stable |\n| External account configuration is rejected | Set id and system together, then compare the id with the Cal.diy account setting |\n| Credential handle configuration is rejected | Provide id, issuer, non-empty scopes, and revision together |\n| Host registers but tenant discovery is empty | Compare the admitted binding, tenant, instance, capability, and route policy before touching the provider secret |\n| Provider returns 401 or 403 | Verify credential type, account, expiry, and permissions; do not retry a mutation with a different secret |\n| Provider returns a redirect | Correct the fixed base URL or upstream configuration; the client deliberately does not follow it with credentials |\n\nRelated documentation\n• Cal.diy connector overview (../README.md)\n• Cal.diy quickstart (quickstart.md)\n• Rotate connector credentials (../../../guides/connector-credentials-and-rotation.md)\n• Connector registry and routing (../../../architecture/connector-registry-and-routing.md)\n• Security model (../../../architecture/security-model.md)\n"
  },
  "integrity": {
    "algorithm": "sha256",
    "sourceDigest": "cd6bd7a684c807a9ae27b26a09bc2bfc85fab4c082dfd5ee4299f0c6083f99df"
  }
}
