> For the complete documentation index, see [llms.txt](https://docs.empe.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.empe.io/develop/wallet-server/did-web-and-hosting.md).

# did:web & Hosting

The Wallet Server creates `did:web` and `did:key` identifiers and serves the resulting `did:web` documents itself over HTTPS. Use `did:web` when you control a domain and want the DID document resolvable at a public URL; use `did:key` when you do not control a domain and want a self-contained identifier with no hosting.

`did:web` documents are stored server-side and published at a public, unauthenticated, throttle-exempt endpoint. The signing keys behind each document are Ed25519.

## How the DID and its URL are built

When you create a `did:web`, you supply the **domain** that hosts it. The server generates a UUID, builds the identifier as `did:web:<domain>:<uuid>`, creates a fresh Ed25519 key that it embeds as the document's verification method (`#key-1`), and stores the resulting document keyed by that UUID.

Following the `did:web` specification, each `:` after the method maps to a `/` in the resolution URL. A bare-domain DID (`did:web:example.com`) would resolve at `/.well-known/did.json`, but because these DIDs carry a path segment (the UUID), resolvers fetch the document at `<path>/did.json`. So `did:web:example.com:<uuid>` resolves to:

```
https://example.com/<uuid>/did.json
```

A non-default port, if any, travels as part of the domain value (for example `example.com:8080`), not as a separate setting.

### Configuration

The effective domain for any given DID always comes from the `domain` field in the create request; there is no operator-level default. The server builds the identifier directly as `did:web:<domain>:<uuid>` from that value, with no separate base path inserted. (See [Environment & Setup](/develop/wallet-server/environment-and-setup.md) for the unused `DID_WEB_DOMAIN`/`DID_WEB_PATH` variables.)

`did:web` requires HTTPS in production; for local development only, set `ALLOW_INSECURE_HTTP=true` to permit `http://`.

## Serving the document

**`GET /:uuid/did.json`** — public, no authentication, exempt from rate limiting. This is the URL a spec-compliant `did:web` resolver fetches for a path-bearing DID.

The Wallet Server also serves the same document at the additional alias **`GET /:uuid/.well-known/did.json`**.

```bash
curl https://example.com/<uuid>/did.json
```

### Response Body

The stored DID document, for example:

```json
{
  "@context": [
    "https://www.w3.org/ns/did/v1",
    "https://w3id.org/security/suites/ed25519-2018/v1"
  ],
  "id": "did:web:example.com:<uuid>",
  "verificationMethod": [
    {
      "id": "did:web:example.com:<uuid>#key-1",
      "type": "Ed25519VerificationKey2018",
      "controller": "did:web:example.com:<uuid>",
      "publicKeyBase58": "..."
    }
  ],
  "authentication": ["did:web:example.com:<uuid>#key-1"],
  "assertionMethod": ["did:web:example.com:<uuid>#key-1"]
}
```

Requesting an unknown UUID returns `404` with the standard error body `{ statusCode, message, path, timestamp }`.

## Creating a hosted did:web

Two routes create a `did:web`. Both require a valid Bearer token (`Authorization: Bearer <token>`) with the `admin` role; in development you may set `AUTH_DISABLED=true`.

### Platform identity

**`POST /agent/did/web`** creates a standalone hosted `did:web` (for example, to back an issuer or verifier).

#### Request Body

* **`domain`** (required) — a valid hostname, optionally with a port, e.g. `issuer.example.com` or `issuer.example.com:8080`.

```bash
curl -X POST https://<wallet-server>/agent/did/web \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{ "domain": "issuer.example.com" }'
```

#### Response Body

* **`did`** — the new identifier, e.g. `did:web:issuer.example.com:<uuid>`.
* **`didDocument`** — the generated DID document (also now served publicly).

### Per-user wallet identity

**`POST /agent/holder`** creates a per-user cloud-wallet tenant. Pass `method: "web"` with a `domain` to back the wallet with a hosted `did:web`; the document is served at the same public URL. Use `method: "key"` for a `did:key` wallet that needs no hosting.

#### Request Body

* **`method`** (required) — `"web"` or `"key"`.
* **`domain`** — required when `method` is `"web"`; ignored for `"key"`.
* **`label`** (optional) — a human-readable name for the wallet.

```bash
curl -X POST https://<wallet-server>/agent/holder \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{ "method": "web", "domain": "example.com", "label": "User Wallet 1" }'
```

#### Response Body

* **`holderId`** — the wallet tenant id.
* **`did`** — the wallet's DID, e.g. `did:web:example.com:<uuid>`.
* **`cryptographicBindingMethodsSupported`**, **`credentialSigningAlgValuesSupported`** — the wallet's binding and signing capabilities.
* **`didDocument`** — present for `did:web` holders.

## did:key as the no-hosting alternative

When you do not control a domain, create a `did:key` instead. It carries its public key inside the identifier, so it is fully self-contained and requires no hosted document or HTTPS endpoint.

**`POST /agent/did/key`** takes no body and returns a `did:key:z6Mk...` identifier with its verification method ids. The same choice is available for wallets via `POST /agent/holder` with `method: "key"`.

```bash
curl -X POST https://<wallet-server>/agent/did/key \
  -H "Authorization: Bearer <token>"
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.empe.io/develop/wallet-server/did-web-and-hosting.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
