ssh
The SSH certificate authority makes Better IAM the authority your servers trust.
The SSH certificate authority makes Better IAM the authority your servers trust. People, service accounts and agents
get short-lived OpenSSH user certificates for exactly the hosts and local accounts their policies allow. Hosts get
certificates their clients verify. Revoked certificates are refused through a key revocation list (KRL) that hosts
fetch every few minutes. The group exists when the deployment sets the ssh option; otherwise every method fails with
FEATURE_DISABLED.
How access is decided
Each enrolled host lists the local accounts people may use. Every pair of host and login is a resource,
ssh-login/{host}/{login}, whose attributes are the host's labels plus host and login. Getting a certificate is
the ssh:login decision on those resources, so conditions, boundaries, just-in-time roles and delegation scopes all
apply. Forwarding is decided on ssh-host/{host} (ssh:port-forward, ssh:agent-forward, ssh:x11-forward). A
certificate allows a kind of forwarding only when every host it names allows it.
A certificate carries one principal per allowed pair, {login}@{host}. Each login's principals file on a host holds
only that host's own principal, so a certificate never opens a host or login it was not issued for.
Administration uses iam:ssh:manage and iam:ssh:read on iam/ssh/.... Hosts call the public methods with their join
token or renewal token. The guide is
SSH access.
| Method | What it does | Access |
|---|---|---|
activateAuthority | Makes a pending authority the signing key. The key it replaces stays trusted as previous, so certificates it issued keep working until they expire. | Credential |
clientTrust | What a member's SSH client needs: known_hosts lines trusting the host authority for the organization's enrolled names (or its patterns), @revoked lines for host keys to refuse, and the host revocation list (base64 KRL) for RevokedHostKeys. Needs a session of the organization. | Credential |
createHost | Registers a server: its name (unique, lowercase letters, digits, . and -, never changes), addresses for the host certificate, logins people may be granted, and labels that policies read as resource.{label}. Returns a one-time join token (shown once) for enrollHost. The name and every address must be unused by other hosts, inside the caller's own iam:ssh:manage scope (decided on iam/ssh/hosts/{address} for each), and names the organization can vouch for: single labels, private addresses, names under its verified domains, and within hostPatterns when set. Needs the authorities from setup. | Credential |
deleteHost | Deletes a host and revokes its certificate. Certificates naming it open nothing afterwards. | Credential |
disableHost | Takes a host out of service. Its certificate is revoked, its key is published as @revoked to clients and in the revocation list, its renewal token stops working, and no user certificate names it any more. | Credential |
enrollHost | A server enrolls with its join token and public host key (not a security key). It gets a host certificate for its name and addresses, the trusted user authority keys, one principals file per login, the revocation list, suggested file paths and an sshd_config drop-in, and the renewal token (once) for syncHost. Public: the join token is the credential. It works once and expires after ssh.joinTokenMs. | Public |
getCertificate | One certificate record: kind, serial, key ID, principals, hosts and logins, extensions, key fingerprint, validity, and revocation details. | Credential |
getHost | One host: status, addresses, logins, labels, host key fingerprint, certificate expiry and when it last synced. Token hashes never appear. | Credential |
getSettings | The tenant's settings (MFA, security keys, source-address binding, default and maximum lifetimes, host patterns), the revocation list version, and the deployment's ceiling for maxCertificateMs. | Credential |
issueCertificate | Certifies the caller's public key (Ed25519, ECDSA, RSA of 2048+ bits, or a FIDO security key) for every requested host and login policies allow (default: every enrolled host). Forwarding extensions are included only where every named host allows them. The lifetime is the requested ttlMs (default from the settings), capped by the tenant maximum, the caller's session and the account's expiry. Returns the certificate line, its principals and validity, the hosts, and known_hosts lines. Works with user sessions, API keys, and temporary and delegated sessions, but never with an impersonation session or a platform root override from outside the organization. | Credential |
listAuthorities | Every authority key of the tenant, retired ones included: kind (user or host), status (pending, active, previous, retired), public key and fingerprint. Private keys never appear. | Credential |
listCertificates | Issued certificates, newest first, filtered by kind, identityId, hostId or status (active, revoked, expired), paged with limit (at most 500) and offset. | Credential |
listHosts | The tenant's hosts by name, filtered by status, exact labels, or a query over names, addresses and descriptions. | Credential |
myAccess | The enrolled hosts the caller may open, with the logins and forwarding policies allow, and the settings that affect a request (MFA, security keys, lifetimes). No permission needed. | Credential |
myCertificates | The caller's own certificates in the tenant, newest first (the last 100), with their status. | Credential |
resetJoinToken | Returns a fresh one-time join token, for a rebuilt server or to bring a disabled host back. The host's renewal token stops working; re-enrolling revokes its previous certificates as superseded. | Credential |
retireAuthority | Stops trusting a previous (or pending) authority. | Credential |
revocationList | A key revocation list, base64. kind: 'user' (the default) is for sshd RevokedKeys: revoked user certificates, certificates of holders who are no longer active or whose session is gone, and every user certificate while the organization is suspended. kind: 'host' is for ssh RevokedHostKeys: revoked host certificates and host keys no enrolled host uses any more. The version increases with every revocation. Public, like an X.509 CRL; read outside any transaction and cached for 10 seconds per tenant, kind and version. | Public |
revokeAllCertificates | Revokes every live user certificate of the tenant, after a suspected compromise. | Credential |
revokeCertificate | Revokes one certificate. Anyone may revoke their own user certificate; revoking someone else's needs the permission. Hosts refuse it from their next revocation list. | Credential |
revokeIdentity | Revokes every live user certificate of one identity, for incident response. | Credential |
rotateAuthority | Starts rotating the user or host authority. A new pending key is published and trusted by hosts and clients from their next sync, and it signs from activateAuthority. With activate: true the new key signs at once (after a compromise); certificates then work only on hosts that have synced since. | Credential |
setup | Creates the tenant's user authority and host authority when they are missing, and returns every authority and the trust material to distribute. Calling it again changes nothing (created is empty). | Credential |
status | The tenant's SSH configuration at a glance: whether it is set up, the settings, the authorities that are not retired, hosts by status, counts of live user and host certificates and of revoked ones, and hosts whose certificate ends within 14 days. | Credential |
sweep | Runs the continuous-authorization sweep for this tenant now. It revokes live certificates whose holder is no longer active, whose temporary session ended, or whose hosts and logins policies no longer allow. The deployment job iam.ssh.sweep() covers every tenant. | Credential |
syncHost | A host's periodic check-in with its renewal token. It returns the current trust, principals files and revocation list, and a new host certificate when the host's addresses or the host authority changed or the current certificate is past two thirds of its lifetime (renew: true forces one). A publicKey other than the enrolled one is refused, so a stolen renewal token cannot certify an attacker's key. Public: the renewal token is the credential. | Public |
trust | The tenant's public trust: user authority keys for hosts (trustedUserCaKeys for TrustedUserCAKeys), host authority keys, and the @cert-authority known_hosts line only when hostPatterns is set. Pending, active and previous keys are all trusted. Public (and bound to the organization's own address): public keys and patterns only, never host names. | Public |
updateHost | Changes a host's addresses, logins, labels or description (null clears it). New addresses reach the host certificate, and new logins the principals files, at the host's next syncHost (syncRequired says so). | Credential |
updateSettings | Changes the tenant's settings; fields left out keep their values. hostPatterns are known_hosts patterns (* and ? globs, ! negation). When set, every host name and address must match them, and the host authority is trusted only for them. Empty patterns trust it for exactly the enrolled names. | Credential |
whoCanLogin | Access review: which active identities may log in to a host, and as which logins. Each identity is decided in a synthetic session (with MFA when assumeMfa), optionally for one login or kind. | Credential |
activateAuthority
Makes a pending authority the signing key. The key it replaces stays trusted as previous, so certificates it issued keep working until they expire.
- Permission:
iam:ssh:manageoniam/ssh/authorities/{kind}. - Errors:
INVALID_TRANSITIONunless the authority is pending.
Prop
Type
A SshAuthorityView object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/activateAuthority" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"authorityId": "<authorityId>"
}'iam.api.ssh.activateAuthority(
credential: CredentialInput,
input: { tenantId: string; authorityId: string },
): Promise<SshAuthorityView>clientTrust
What a member's SSH client needs: known_hosts lines trusting the host authority for the organization's enrolled names (or its patterns), @revoked lines for host keys to refuse, and the host revocation list (base64 KRL) for RevokedHostKeys. Needs a session of the organization.
Prop
Type
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/clientTrust" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.ssh.clientTrust(
credential: CredentialInput,
input: { tenantId: string },
): Promise<{ knownHosts: string; revokedHostKeys: string; hostPatterns: string[] }>createHost
Registers a server: its name (unique, lowercase letters, digits, . and -, never changes), addresses for the host certificate, logins people may be granted, and labels that policies read as resource.{label}. Returns a one-time join token (shown once) for enrollHost. The name and every address must be unused by other hosts, inside the caller's own iam:ssh:manage scope (decided on iam/ssh/hosts/{address} for each), and names the organization can vouch for: single labels, private addresses, names under its verified domains, and within hostPatterns when set. Needs the authorities from setup.
Used inSSH access
- Permission:
iam:ssh:manageoniam/ssh/hosts/{name}. - Errors:
CONFLICTfor a name in use;HOST_NAME_TAKENfor an address another host uses;HOST_OUTSIDE_PATTERNSfor a name or address the organization cannot vouch for;ACCESS_DENIEDfor an address outside the caller's scope;SSH_NOT_CONFIGURED;INVALID_INPUTfor invalid names, logins, addresses or labels. - Audited as:
iam:ssh:manage; refusals by name as a deniediam:ssh:managewithmetadata.reason.
A SshHostInput object:
Prop
Type
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/createHost" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"name": "<name>",
"logins": [
"<login>"
]
}'iam.api.ssh.createHost(
credential: CredentialInput,
input: SshHostInput,
): Promise<{ host: SshHostView; joinToken: string; joinTokenExpiresAt: number }>deleteHost
Deletes a host and revokes its certificate. Certificates naming it open nothing afterwards.
- Permission:
iam:ssh:manageoniam/ssh/hosts/{name}.
Prop
Type
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/deleteHost" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"hostId": "<hostId>"
}'iam.api.ssh.deleteHost(
credential: CredentialInput,
input: { tenantId: string; hostId: string },
): Promise<{ deleted: boolean }>disableHost
Takes a host out of service. Its certificate is revoked, its key is published as @revoked to clients and in the revocation list, its renewal token stops working, and no user certificate names it any more.
- Permission:
iam:ssh:manageoniam/ssh/hosts/{name}.
Prop
Type
A SshHostView object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/disableHost" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"hostId": "<hostId>"
}'iam.api.ssh.disableHost(
credential: CredentialInput,
input: { tenantId: string; hostId: string },
): Promise<SshHostView>enrollHost
A server enrolls with its join token and public host key (not a security key). It gets a host certificate for its name and addresses, the trusted user authority keys, one principals file per login, the revocation list, suggested file paths and an sshd_config drop-in, and the renewal token (once) for syncHost. Public: the join token is the credential. It works once and expires after ssh.joinTokenMs.
- Audited as:
ssh:host:enroll(actorssh-host:{hostId}). - Errors:
INVALID_TOKENfor an unknown, used or expired token or a disabled host (audited as a deniedssh:host:enrollwhen the host exists);HOST_KEY_IN_USEfor another host's key;INVALID_INPUTfor an authority key or a security key;TENANT_INACTIVE;RATE_LIMITED.
Prop
Type
A SshHostSetup object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/enrollHost" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"joinToken": "<joinToken>",
"publicKey": "<publicKey>"
}'iam.api.ssh.enrollHost(
input: { joinToken: string; publicKey: string },
): Promise<SshHostSetup>getCertificate
One certificate record: kind, serial, key ID, principals, hosts and logins, extensions, key fingerprint, validity, and revocation details.
- Permission:
iam:ssh:readoniam/ssh/certificates/{id}.
Prop
Type
A SshCertificateView object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/getCertificate" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"certificateId": "<certificateId>"
}'iam.api.ssh.getCertificate(
credential: CredentialInput,
input: { tenantId: string; certificateId: string },
): Promise<SshCertificateView>getHost
One host: status, addresses, logins, labels, host key fingerprint, certificate expiry and when it last synced. Token hashes never appear.
- Permission:
iam:ssh:readoniam/ssh/hosts/{name}.
Prop
Type
A SshHostView object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/getHost" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"hostId": "<hostId>"
}'iam.api.ssh.getHost(
credential: CredentialInput,
input: { tenantId: string; hostId: string },
): Promise<SshHostView>getSettings
The tenant's settings (MFA, security keys, source-address binding, default and maximum lifetimes, host patterns), the revocation list version, and the deployment's ceiling for maxCertificateMs.
- Permission:
iam:ssh:readoniam/ssh/settings.
Prop
Type
A SshSettingsView object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/getSettings" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.ssh.getSettings(
credential: CredentialInput,
input: { tenantId: string },
): Promise<SshSettingsView>issueCertificate
Certifies the caller's public key (Ed25519, ECDSA, RSA of 2048+ bits, or a FIDO security key) for every requested host and login policies allow (default: every enrolled host). Forwarding extensions are included only where every named host allows them. The lifetime is the requested ttlMs (default from the settings), capped by the tenant maximum, the caller's session and the account's expiry. Returns the certificate line, its principals and validity, the hosts, and known_hosts lines. Works with user sessions, API keys, and temporary and delegated sessions, but never with an impersonation session or a platform root override from outside the organization.
Used inSSH access
- Permission: a session;
ssh:loginon eachssh-login/{host}/{login}decides. - Audited as:
ssh:certificate:issuewith the serial, key ID, hosts, fingerprint and reason; a refusal is a deniedssh:loginevent withmetadata.reason. - Errors:
ACCESS_DENIED;NOT_FOUNDfor a named host that is not enrolled;MFA_REQUIRED;SECURITY_KEY_REQUIRED;SOURCE_ADDRESS_UNKNOWN;TOO_MANY_HOSTS;SESSION_EXPIRING;IMPERSONATION_RESTRICTED;ROOT_SSH_RESTRICTED;SSH_NOT_CONFIGURED.
const cert = await iam.api.ssh.issueCertificate(session, {
tenantId,
publicKey: 'ssh-ed25519 AAAA... alice@laptop',
hosts: ['web-01'],
ttlMs: 30 * 60_000,
reason: 'hotfix 42',
});
// Save cert.certificate as ~/.ssh/id_ed25519-cert.pubA SshCertificateRequest object:
Prop
Type
A SshIssuedCertificate object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/issueCertificate" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"publicKey": "<publicKey>"
}'iam.api.ssh.issueCertificate(
credential: CredentialInput,
input: SshCertificateRequest,
): Promise<SshIssuedCertificate>listAuthorities
Every authority key of the tenant, retired ones included: kind (user or host), status (pending, active, previous, retired), public key and fingerprint. Private keys never appear.
- Permission:
iam:ssh:readoniam/ssh/authorities.
Prop
Type
An array of SshAuthorityView.
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/listAuthorities" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.ssh.listAuthorities(
credential: CredentialInput,
input: { tenantId: string },
): Promise<SshAuthorityView[]>listCertificates
Issued certificates, newest first, filtered by kind, identityId, hostId or status (active, revoked, expired), paged with limit (at most 500) and offset.
- Permission:
iam:ssh:readoniam/ssh/certificates.
Prop
Type
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/listCertificates" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.ssh.listCertificates(
credential: CredentialInput,
input: {
tenantId: string;
kind?: 'user' | 'host';
identityId?: string;
hostId?: string;
status?: 'active' | 'revoked' | 'expired';
limit?: number;
offset?: number;
},
): Promise<{ certificates: SshCertificateView[]; total: number }>listHosts
The tenant's hosts by name, filtered by status, exact labels, or a query over names, addresses and descriptions.
- Permission:
iam:ssh:readoniam/ssh/hosts.
Prop
Type
An array of SshHostView.
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/listHosts" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.ssh.listHosts(
credential: CredentialInput,
input: {
tenantId: string;
status?: SshHost['status'];
labels?: Record<string, string>;
query?: string;
},
): Promise<SshHostView[]>myAccess
The enrolled hosts the caller may open, with the logins and forwarding policies allow, and the settings that affect a request (MFA, security keys, lifetimes). No permission needed.
Prop
Type
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/myAccess" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.ssh.myAccess(
credential: CredentialInput,
input: { tenantId: string },
): Promise<{
hosts: SshAccessEntry[];
requireMfa: boolean;
requireSecurityKey: boolean;
defaultCertificateMs: number;
maxCertificateMs: number;
}>myCertificates
The caller's own certificates in the tenant, newest first (the last 100), with their status.
Prop
Type
An array of SshCertificateView.
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/myCertificates" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.ssh.myCertificates(
credential: CredentialInput,
input: { tenantId: string },
): Promise<SshCertificateView[]>resetJoinToken
Returns a fresh one-time join token, for a rebuilt server or to bring a disabled host back. The host's renewal token stops working; re-enrolling revokes its previous certificates as superseded.
- Permission:
iam:ssh:manageoniam/ssh/hosts/{name}.
Prop
Type
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/resetJoinToken" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"hostId": "<hostId>"
}'iam.api.ssh.resetJoinToken(
credential: CredentialInput,
input: { tenantId: string; hostId: string },
): Promise<{ host: SshHostView; joinToken: string; joinTokenExpiresAt: number }>retireAuthority
Stops trusting a previous (or pending) authority.
- Permission:
iam:ssh:manageoniam/ssh/authorities/{kind}. - Errors:
RESOURCE_IN_USEwhile certificates it signed are still valid, unlessforce: true, which revokes them;INVALID_TRANSITIONfor the active authority.
Prop
Type
A SshAuthorityView object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/retireAuthority" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"authorityId": "<authorityId>"
}'iam.api.ssh.retireAuthority(
credential: CredentialInput,
input: { tenantId: string; authorityId: string; force?: boolean },
): Promise<SshAuthorityView>revocationList
A key revocation list, base64. kind: 'user' (the default) is for sshd RevokedKeys: revoked user certificates, certificates of holders who are no longer active or whose session is gone, and every user certificate while the organization is suspended. kind: 'host' is for ssh RevokedHostKeys: revoked host certificates and host keys no enrolled host uses any more. The version increases with every revocation. Public, like an X.509 CRL; read outside any transaction and cached for 10 seconds per tenant, kind and version.
Prop
Type
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/revocationList" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.ssh.revocationList(
input: { tenantId: string; kind?: 'user' | 'host' },
): Promise<{
tenantId: string;
kind: 'user' | 'host';
version: number;
generatedAt: number;
tenantActive: boolean;
revokedCertificates: number;
revokedHostKeys: number;
krl: string;
}>revokeAllCertificates
Revokes every live user certificate of the tenant, after a suspected compromise.
- Permission:
iam:ssh:manageoniam/ssh/certificates.
Prop
Type
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/revokeAllCertificates" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.ssh.revokeAllCertificates(
credential: CredentialInput,
input: { tenantId: string },
): Promise<{ revoked: number }>revokeCertificate
Revokes one certificate. Anyone may revoke their own user certificate; revoking someone else's needs the permission. Hosts refuse it from their next revocation list.
- Permission: none for your own; otherwise
iam:ssh:manageoniam/ssh/certificates/{id}. - Audited as:
ssh:certificate:revoke(own) oriam:ssh:manage.
Prop
Type
A SshCertificateView object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/revokeCertificate" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"certificateId": "<certificateId>"
}'iam.api.ssh.revokeCertificate(
credential: CredentialInput,
input: { tenantId: string; certificateId: string },
): Promise<SshCertificateView>revokeIdentity
Revokes every live user certificate of one identity, for incident response.
- Permission:
iam:ssh:manageoniam/ssh/identities/{identityId}.
Prop
Type
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/revokeIdentity" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"identityId": "<identityId>"
}'iam.api.ssh.revokeIdentity(
credential: CredentialInput,
input: { tenantId: string; identityId: string },
): Promise<{ revoked: number }>rotateAuthority
Starts rotating the user or host authority. A new pending key is published and trusted by hosts and clients from their next sync, and it signs from activateAuthority. With activate: true the new key signs at once (after a compromise); certificates then work only on hosts that have synced since.
- Permission:
iam:ssh:manageoniam/ssh/authorities/{kind}. - Errors:
CONFLICTwhile a rotation is already pending.
Prop
Type
A SshAuthorityView object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/rotateAuthority" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"kind": "user"
}'iam.api.ssh.rotateAuthority(
credential: CredentialInput,
input: { tenantId: string; kind: SshAuthorityKind; activate?: boolean },
): Promise<SshAuthorityView>setup
Creates the tenant's user authority and host authority when they are missing, and returns every authority and the trust material to distribute. Calling it again changes nothing (created is empty).
Used inSSH access
- Permission:
iam:ssh:manageoniam/ssh/authorities. - Audited as:
iam:ssh:manage.
Prop
Type
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/setup" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.ssh.setup(
credential: CredentialInput,
input: { tenantId: string },
): Promise<{
created: SshAuthorityKind[];
authorities: SshAuthorityView[];
trust: {
tenantId: string;
userAuthorities: {
id: string;
status: SshAuthorityStatus;
publicKey: string;
fingerprint: string;
}[];
hostAuthorities: {
id: string;
status: SshAuthorityStatus;
publicKey: string;
fingerprint: string;
}[];
hostPatterns: string[];
trustedUserCaKeys: string;
knownHosts: string;
};
}>status
The tenant's SSH configuration at a glance: whether it is set up, the settings, the authorities that are not retired, hosts by status, counts of live user and host certificates and of revoked ones, and hosts whose certificate ends within 14 days.
- Permission:
iam:ssh:readoniam/ssh/settings.
Prop
Type
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/status" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.ssh.status(
credential: CredentialInput,
input: { tenantId: string },
): Promise<{
configured: boolean;
settings: SshSettingsView;
authorities: SshAuthorityView[];
hosts: { pending: number; enrolled: number; disabled: number };
certificates: { activeUser: number; activeHost: number; revoked: number };
hostsExpiringSoon: number;
}>sweep
Runs the continuous-authorization sweep for this tenant now. It revokes live certificates whose holder is no longer active, whose temporary session ended, or whose hosts and logins policies no longer allow. The deployment job iam.ssh.sweep() covers every tenant.
- Permission:
iam:ssh:manageoniam/ssh/certificates. - Audited as:
ssh:certificate:revokeper revoked certificate (actordeployment-operator).
Prop
Type
A SshSweepResult object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/sweep" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.ssh.sweep(
credential: CredentialInput,
input: { tenantId: string },
): Promise<SshSweepResult>syncHost
A host's periodic check-in with its renewal token. It returns the current trust, principals files and revocation list, and a new host certificate when the host's addresses or the host authority changed or the current certificate is past two thirds of its lifetime (renew: true forces one). A publicKey other than the enrolled one is refused, so a stolen renewal token cannot certify an attacker's key. Public: the renewal token is the credential.
-
Audited as:
ssh:host:renewwhen a certificate is issued. A renewed certificate comes with a newrenewalToken; the one presented keeps working until the new one is used.renew: trueis honoured at most hourly. While the organization is suspended the host still syncs, without a new certificate. -
Errors:
INVALID_TOKEN;HOST_KEY_CHANGED(re-enroll with a new join token; audited as a deniedssh:host:sync);RATE_LIMITED.
Prop
Type
A SshHostSetup object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/syncHost" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"renewalToken": "<renewalToken>"
}'iam.api.ssh.syncHost(
input: { renewalToken: string; publicKey?: string; renew?: boolean },
): Promise<SshHostSetup>trust
The tenant's public trust: user authority keys for hosts (trustedUserCaKeys for TrustedUserCAKeys), host authority keys, and the @cert-authority known_hosts line only when hostPatterns is set. Pending, active and previous keys are all trusted. Public (and bound to the organization's own address): public keys and patterns only, never host names.
Prop
Type
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/trust" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.ssh.trust(
input: { tenantId: string },
): Promise<{
tenantId: string;
userAuthorities: {
id: string;
status: SshAuthorityStatus;
publicKey: string;
fingerprint: string;
}[];
hostAuthorities: {
id: string;
status: SshAuthorityStatus;
publicKey: string;
fingerprint: string;
}[];
hostPatterns: string[];
trustedUserCaKeys: string;
knownHosts: string;
}>updateHost
Changes a host's addresses, logins, labels or description (null clears it). New addresses reach the host certificate, and new logins the principals files, at the host's next syncHost (syncRequired says so).
- Permission:
iam:ssh:manageoniam/ssh/hosts/{name}. - Errors:
HOST_OUTSIDE_PATTERNS.
A SshHostUpdate object:
Prop
Type
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/updateHost" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"hostId": "<hostId>"
}'iam.api.ssh.updateHost(
credential: CredentialInput,
input: SshHostUpdate,
): Promise<{ host: SshHostView; syncRequired: boolean }>updateSettings
Changes the tenant's settings; fields left out keep their values. hostPatterns are known_hosts patterns (* and ? globs, ! negation). When set, every host name and address must match them, and the host authority is trusted only for them. Empty patterns trust it for exactly the enrolled names.
- Permission:
iam:ssh:manageoniam/ssh/settings. - Errors:
INVALID_INPUTfor lifetimes outside one minute to the deployment maximum, a default above the maximum, or malformed patterns;HOST_OUTSIDE_PATTERNSwhen the new patterns leave an existing host uncovered.
Prop
Type
A SshSettingsView object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/updateSettings" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.ssh.updateSettings(
credential: CredentialInput,
input: {
tenantId: string;
requireMfa?: boolean;
requireSecurityKey?: boolean;
requireUserVerification?: boolean;
bindSourceAddress?: boolean;
defaultCertificateMs?: number;
maxCertificateMs?: number;
hostPatterns?: string[];
},
): Promise<SshSettingsView>whoCanLogin
Access review: which active identities may log in to a host, and as which logins. Each identity is decided in a synthetic session (with MFA when assumeMfa), optionally for one login or kind.
- Permission:
iam:ssh:readoniam/ssh/hosts/{name}.
Prop
Type
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/ssh/whoCanLogin" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"hostId": "<hostId>"
}'iam.api.ssh.whoCanLogin(
credential: CredentialInput,
input: {
tenantId: string;
hostId: string;
login?: string;
assumeMfa?: boolean;
kind?: 'user' | 'service' | 'agent';
},
): Promise<{
host: string;
identities: {
identityId: string;
name?: string;
email?: string;
kind: string;
logins: string[];
}[];
}>Better IAM is created by Sean Filimon
Last updated