Skip to main content

Service DIDs

A Service DID is a decentralised identity for one of an organisation's systems: an API, an automated process, a pipeline or an AI agent. It does not represent the organisation itself, but something that acts on its behalf.

Every Service DID depends on an organisational did:isbe already registered in ISBE, which we call the umbrella DID. The umbrella creates it, is the only one that can change it, and is accountable for it.

What is it for?​

An organisation registered in ISBE has its DID and its control key. If its systems signed with that same key, a failure in any one of them would compromise the identity of the whole organisation. Service DIDs solve that:

  • Each system has its own key. The organisation's key never leaves the place where it is kept.
  • If a service's key is compromised, that service's key is rotated or the service is deactivated, without touching the organisation's DID or any of its other services.
  • You know who signed. A signature made by a service can be attributed to that service and to the organisation that controls it.
  • Each service can publish information about itself (endpoints, version, contact details…) in its DID Document, which anyone can look up.

Some examples:

SystemService DID
An e-invoicing APISigns the invoices it issues
A nightly reconciliation processSigns the reports it produces
An AI agent that handles requestsIdentifies itself to other systems when it acts
A credential issuerPublishes its OpenID4VCI endpoint in the DID Document

Identifier format​

did:isbe:svc:<modelDeploy>:<umbrella identifier>:<n>
  • svc marks it as a Service DID rather than an organisational DID.
  • <modelDeploy> is the environment: uc-pre in pre-production and uc in production, as in the umbrella DID.
  • <umbrella identifier> is the last part of the organisational DID that controls it.
  • <n> is a counter of each umbrella: its first service is 1, the second 2, and so on.

For example, the third service of the organisation did:isbe:uc-pre:z1MsSK9H41YvdCrJPmPycEqijYbk is:

did:isbe:svc:uc-pre:z1MsSK9H41YvdCrJPmPycEqijYbk:3

Since the umbrella's identifier is part of the Service DID, reading it is enough to know which organisation controls it.

How it works​

The chain of trust is the same as for the organisational DID: the organisation identifies itself when it registers in ISBE, and its services inherit that trust through the umbrella. No new governance is introduced.

The umbrella controls everything​

  • Only the umbrella can create, change or deactivate its Service DIDs. All those operations must be signed by an account with active capability invocation on the umbrella DID: the same one you use to manage your organisational DID.
  • A service's key cannot change its own Service DID. It is there for the service to sign its own work, not to manage its identity.
  • If an umbrella has several controllers, any of them can manage its services.

The API holds no keys​

Like the DID Registry API, the Service DID Registry API does not hold private keys. Every write operation returns an unsigned transaction. You sign it on your own computer with the isbe-identity-did-gen CLI and send it to the API, which publishes it on the network. Your private key never leaves your computer.

Service DIDs are stored in the same contract as organisational DIDs: the Diamond 0x00000000000000000000000000000000000015BE.

The service key​

Each Service DID has a secp256k1 signing key. You generate it with did-gen keys, just like the key of your organisational DID, but it is a different key.

Can this key be used for a service?
A new key, generated for the service✅ Yes
The same key as another service of the same umbrella✅ Yes: services of the same umbrella can share a key
The key of any organisational DID, including the umbrella itself❌ No
The key of a service of another umbrella❌ No

A key stays bound forever to the umbrella of the first service that used it, even if that service later rotates to another key or is deactivated. This way, every signature can be attributed unambiguously to a single organisation.

Sharing a key between services

Sharing a key is useful when several services run on the same machine. Bear in mind that, if they share a key, a signature tells you which organisation made it, but not which of those services. If you need to tell them apart, give each one its own key.

Free-form fields​

On top of the minimum data, when you register a Service DID you can add any fields you want: the service's name, a description, organisation details, contact details, version, endpoints… They are stored on-chain and appear in the service's DID Document.

A typical case is the service property of the W3C DID Core standard (§5.4), which publishes the service's endpoints:

"service": [
{
"id": "#api",
"type": "InvoicingAPI",
"serviceEndpoint": "https://api.facturacion.ejemplo.es/v2"
}
]

Rules for free-form fields:

  • Maximum size: 4096 bytes in total.
  • You cannot use the fields the registry itself generates: @context, id, controller, verificationMethod, authentication, assertionMethod, capabilityInvocation, capabilityDelegation, keyAgreement, deactivated and reason.
  • When you change them, they are replaced as a whole: the new fields replace all the previous ones; they are not merged.
Free-form fields are public

Everything you put in the free-form fields stays on the blockchain, visible to anyone, and its history cannot be erased. Do not include personal data, secrets or confidential information.

The DID Document of a Service DID​

Resolving a Service DID returns a W3C DID Document. For example:

{
"@context": [
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/suites/secp256k1recovery-2020/v2"
],
"id": "did:isbe:svc:uc-pre:z1MsSK9H41YvdCrJPmPycEqijYbk:3",
"controller": ["did:isbe:uc-pre:z1MsSK9H41YvdCrJPmPycEqijYbk"],
"verificationMethod": [
{
"id": "did:isbe:svc:uc-pre:z1MsSK9H41YvdCrJPmPycEqijYbk:3#key-1",
"type": "EcdsaSecp256k1RecoveryMethod2020",
"controller": "did:isbe:svc:uc-pre:z1MsSK9H41YvdCrJPmPycEqijYbk:3",
"blockchainAccountId": "eip155:<chainId>:0x<address of the service key>"
}
],
"authentication": ["did:isbe:svc:uc-pre:z1MsSK9H41YvdCrJPmPycEqijYbk:3#key-1"],
"assertionMethod": ["did:isbe:svc:uc-pre:z1MsSK9H41YvdCrJPmPycEqijYbk:3#key-1"],

"name": "E-invoicing service",
"service": [
{
"id": "#api",
"type": "InvoicingAPI",
"serviceEndpoint": "https://api.facturacion.ejemplo.es/v2"
}
]
}
  • controller is the umbrella DID.
  • verificationMethod holds the service key, identified by the address derived from it (blockchainAccountId).
  • The service's free-form fields follow, in this example name and service.

When it stops being valid​

A Service DID is active as long as none of these conditions applies. If one does, the resolved DID Document includes "deactivated": true and a reason saying why:

reasonWhat happened
deactivatedThe umbrella deactivated it. This is final: it cannot be reactivated.
expiredIts expiry date (expiresAt) has passed. The umbrella can extend it.
controller-inactiveThe umbrella DID no longer has any key with active capability invocation. All its services stop being valid at once.

When you register a service you can set its expiry, or leave it at 0, which means it never expires.

Next steps​