Device Credentials
A device is a physical or soft phone that registers to the platform and rings when its line is called. Declaring devices and binding them to lines is config work, covered in Lines; this page covers the one piece of device state that lives outside the config document: the SIP digest credential each device authenticates with. The API surface is deliberately small — two verbs on one endpoint — but the lifecycle around it has rules worth knowing.
Conventions as usual: $VSS, $TOKEN for curl; a configured vsscli
profile. Peer commands are top-level (vsscli peer …) — unlike voicemail
and line features they are not namespace-scoped, because the peer key
itself identifies the tenant.
The model — devices are SIP peers
Section titled “The model — devices are SIP peers”Every device in an applied config becomes a SIP peer whose key is:
<device-name>@<sip-realm>sip-realm is the namespace-level realm from the config’s
configuration: block. The realm is what lets tenants share device and
extension names — alice-desk@pbx.acme.example and
alice-desk@pbx.other.example are different peers — and it is the realm
your devices must be configured to authenticate against.
The credential lifecycle is tied to config applies:
- Apply that adds a device → the peer row is created without a credential. A device cannot register until a password is minted through the endpoints below — new devices are dark by default, mirroring the no-default-PIN stance voicemail takes.
- Apply that removes a device → the peer row is deleted, and deliberately in auth-before-routing order: a removed device loses the ability to authenticate before anything else is torn down. There is no orphan state to clean up.
- Password writes never touch config — no version, no apply, effective on their own.
The server stores only the digest hash (HA1), never the cleartext. There is consequently no “read password” endpoint and no recovery: a lost device password is replaced, not retrieved.
Permissions
Section titled “Permissions”Credential writes are gated by the peer:setpass action, held by
partition-admin, namespace-admin, and provisioner. A peer is
authorized against its owning namespace, the same way a line is, so a
grant qualifies when it covers that namespace:
- A namespace-scoped grant works for peers in that namespace (or any
descendant — subtree breadth applies, just like line and voicemail
authorization). A namespace-scoped
provisionerornamespace-admincan mint credentials for the devices in their own namespace(s). - A partition- or system-scoped grant works for every peer in that partition. Use this for a central device-provisioning credential.
operator/editordeliberately do not carry the action — a peer credential change can knock a registered device offline, so it stays with the admin/provisioning roles.
Two practical recipes — a namespace provisioner for one tenant, or a partition-wide provisioning credential:
vsscli user grant add <prov-user-id> --role provisioner --scope ns:$NSIDvsscli user grant add <prov-user-id> --role provisioner --scope pt:$PTIDMinting a credential
Section titled “Minting a credential”Two ways to set a password. Generate is preferred — the server mints a strong random passphrase and returns it exactly once:
curl -s -X POST "$VSS/v1/peer/alice-desk@pbx.acme.example/setpass" \ -H "Authorization: Bearer $TOKEN"# → {"success":true,"password":"kD8mQv2xT9wRfLpN3sJh"}Capture password from the response — it is not stored and cannot be
shown again.
vsscli peer password generate alice-desk@pbx.acme.example --showBecause the value is shown once, vsscli refuses to print it to an interactive terminal unless you’re explicit. Pick a capture mode:
--show # print to the terminal--copy # clipboard--out creds.txt # file, mode 0600--json # {"password":"…"} for pipelines--env VSS_PEER_PW # print an export line for evalSet stores a password you chose — for staged provisioning systems that pre-print credentials or phones with fixed passwords:
curl -s -X PUT "$VSS/v1/peer/alice-desk@pbx.acme.example/setpass" \ -H "Authorization: Bearer $TOKEN" -d '{"password":"chosen-by-you"}'# → 204 No Contentvsscli peer password set alice-desk@pbx.acme.example # prompts twice, no echovsscli peer password set alice-desk@pbx.acme.example --password '…' # scriptedBoth verbs are also the rotation path — setting a password on a peer that already has one simply replaces it.
The device then registers with:
| Setting on the phone | Value |
|---|---|
| Username / auth user | The device name (alice-desk) |
| Realm / domain | The namespace’s sip-realm |
| Password | What you just minted |
| Registrar / proxy | The namespace’s anycast IP |
Rotation semantics — what happens to a live device
Section titled “Rotation semantics — what happens to a live device”- The new password works immediately. Edge proxies verify against the stored HA1, fetching it on demand.
- The old password can linger for up to ~5 minutes. Each edge node keeps a short-lived credential cache (5-minute expiry); a device re-authenticating with the old password against a node that still holds the old hash will succeed until that cache entry expires. Rotation is therefore fast but not instant revocation.
- Registrations don’t drop on rotation. An existing registration stays valid until it expires; the device feels the change at its next re-REGISTER, when it must present the new password.
For a compromised credential, rotate immediately and treat the ~5-minute cache window as the exposure bound on the credential itself. To cut a device off entirely without touching config, revoke the credential (below); to remove the device altogether, delete it from the config and apply.
Revoking a credential — the kill switch
Section titled “Revoking a credential — the kill switch”Revoke resets the stored HA1 to empty. An empty credential can never authenticate (the auth path fails closed on an empty secret), so revoke is the way to disable a lost or compromised device without editing and re-applying config. Setting a new password later re-enables it.
curl -s -X PUT "$VSS/v1/peer/alice-desk@pbx.acme.example/setpass?clear=true" \ -H "Authorization: Bearer $TOKEN"# → 204 No Content (no request body)vsscli peer password set alice-desk@pbx.acme.example --clearRevoke is subject to the same ~5-minute edge cache window as rotation:
a node still holding the old hash will accept the old password until its
cache entry expires. The difference from rotation is that no valid
credential exists afterward — nothing can register as the device until you
mint a new one. Same peer:setpass permission as setting a password.
Onboarding a device, end to end
Section titled “Onboarding a device, end to end”# 1. Config: declare the device and bind it to a line (then apply)# devices:# alice-desk:# line: "1001"# lines:# "1001":# devices: [alice-desk]
# 2. Mint its credentialvsscli peer password generate alice-desk@pbx.acme.example --out alice-desk.cred
# 3. Configure the phone (username / realm / password / registrar) and# watch it register; then place a test call in each direction.Order matters only in one direction: the peer must exist (step 1 applied)
before step 2 — setpass on an unknown peer is a 404, it never creates
one.
Troubleshooting
Section titled “Troubleshooting”| Response / symptom | Meaning | Do |
|---|---|---|
400 invalid peer key |
The {peer} path segment isn’t name@realm |
Include the realm — the bare device name is not a peer key |
404 SIP peer not found |
No such peer row | The device isn’t in an applied config yet, was removed, or the realm half doesn’t match the namespace’s sip-realm |
401 on setpass |
The caller’s grant doesn’t cover the peer’s owning namespace, or the role lacks peer:setpass (e.g. operator/editor) |
Grant provisioner/namespace-admin on the peer’s namespace (ns:$NSID), or a partition grant (pt:$PTID) that contains it |
| Old password still registers after rotation | Per-edge credential cache | Expected for up to ~5 minutes; see rotation semantics above |
| Device won’t register with a fresh credential | Realm mismatch is the usual culprit | The phone must send the device name as auth user and the namespace’s exact sip-realm as realm/domain |