---
title: "domains.proposal@1 · OpenVibe.Services"
canonical: "https://openvibe.services/docs/contracts/domains.proposal"
updated: null
---

# domains.proposal@1 · OpenVibe.Services

HTML: [domains.proposal@1 · OpenVibe.Services](https://openvibe.services/docs/contracts/domains.proposal)

# DomainsProposal `domains.proposal@1`

Generated at 2026-11-13 06:25:48 UTC from [`openvibe-contracts v0.419.0`](https://github.com/OpenVibers/OpenVibe.Contracts/tree/v0.419.0) and [`openvibe-sdk v0.161.0`](https://github.com/OpenVibers/OpenVibe.SDK/tree/v0.161.0).

- Version: 1.0.0
- Owner: domains
- Visibility: public
- Status: active
- Compatibility: backward
- Schema: [`https://openvibe.network/contracts/domains/proposal.v1.json`](/docs/contracts/domains.proposal.json)

A change to a person's DNS on OpenVibe.Domains (plan 2026-10-28 §5.1): nothing an agent, a bulk edit or the person's own edit asks for touches DNS until the person approves it. A proposal holds the record diff, why, what could break, and its rollback; approving it applies the changes in order through the provider's API with the person's connected credential (a Network project secret, ADR-056, never part of this document). A change whose record moved since the proposal was made is refused as stale; rolling back applies the inverse changes.

## Fields

| Field | Type | Required | Description | Constraints |
| --- | --- | --- | --- | --- |
| `id` | string | yes |  | - `pattern ^dpr_[0-9A-HJKMNP-TV-Z]{26}$` |
| `domain` | string | yes |  | - `minLength 3`<br>- `maxLength 253` |
| `provider` | enum | yes |  | - `one of "cloudflare", "porkbun", "namecheap", "spaceship"` |
| `connection_id` | string | yes |  | - `pattern ^dcn_[0-9A-HJKMNP-TV-Z]{26}$` |
| `title` | string | yes |  | - `minLength 1`<br>- `maxLength 160` |
| `why` | string \| null |  |  | - `maxLength 1000` |
| `risk` | string \| null |  | What could break, in plain words. | - `maxLength 1000` |
| `status` | enum | yes |  | - `one of "pending", "applying", "applied", "declined", "failed", "rolled_back", "expired"` |
| `changes` | array of object | yes |  | - `minItems 1`<br>- `maxItems 50`<br>- `items: no other fields` |
| `changes[].op` | enum | yes |  | - `one of "create", "update", "delete"` |
| `changes[].record_id` | string \| null |  | The provider's record id (update, delete; a create's after it is applied). | - `maxLength 64` |
| `changes[].before` | one of |  |  |  |
| `changes[].after` | one of |  |  |  |
| `changes[].type` | enum |  | A registrar change: the domain's nameservers at the registry. op is update only; before and after are the lists. | - `one of "NS"` |
| `created_by` | string | yes |  | - `pattern ^(user\|agent\|service):`<br>- `maxLength 120` |
| `created_at` | string | yes |  | - `format date-time` |
| `decided_at` | string \| null |  |  | - `format date-time` |
| `applied_at` | string \| null |  |  | - `format date-time` |
| `rolled_back_at` | string \| null |  |  | - `format date-time` |
| `expires_at` | string \| null |  |  | - `format date-time` |
| `error` | string \| null |  |  | - `maxLength 400` |
| `href` | string | yes |  | - `pattern ^https://`<br>- `format uri` |

## Examples

From the contract's own test fixtures: valid ones validate, rejected ones must fail.

 Valid: create-txt

~~~~
{
  "id": "dpr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3",
  "domain": "example.com",
  "provider": "cloudflare",
  "connection_id": "dcn_01J8Z3Q4R5S6T7V8W9X0Y1Z2A4",
  "title": "Add the TXT record that proves example.com is yours",
  "why": "Your mail provider asks for it.",
  "risk": "None: a TXT record changes no traffic.",
  "status": "pending",
  "changes": [
    {
      "op": "create",
      "record_id": null,
      "before": null,
      "after": {
        "type": "TXT",
        "name": "example.com",
        "content": "proof=abc123",
        "ttl": 1,
        "proxied": null,
        "priority": null,
        "comment": null
      }
    }
  ],
  "created_by": "user:usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
  "created_at": "2026-10-28T18:00:00.000Z",
  "decided_at": null,
  "applied_at": null,
  "rolled_back_at": null,
  "expires_at": "2026-11-04T18:00:00.000Z",
  "error": null,
  "href": "https://openvibe.domains/panel/proposals/dpr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3"
}
~~~~

Valid: nameservers

~~~~
{
  "id": "dpr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3",
  "domain": "example.com",
  "provider": "porkbun",
  "connection_id": "dcn_01J8Z3Q4R5S6T7V8W9X0Y1Z2A4",
  "title": "Point example.com at Cloudflare",
  "why": "Your mail provider asks for it.",
  "risk": "None: a TXT record changes no traffic.",
  "status": "pending",
  "changes": [
    {
      "op": "update",
      "type": "NS",
      "record_id": null,
      "before": [
        "curitiba.ns.porkbun.com",
        "fortaleza.ns.porkbun.com"
      ],
      "after": [
        "ada.ns.cloudflare.com",
        "bob.ns.cloudflare.com"
      ]
    }
  ],
  "created_by": "user:usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
  "created_at": "2026-10-28T18:00:00.000Z",
  "decided_at": null,
  "applied_at": null,
  "rolled_back_at": null,
  "expires_at": "2026-11-04T18:00:00.000Z",
  "error": null,
  "href": "https://openvibe.domains/panel/proposals/dpr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3"
}
~~~~

Valid: update-applied

~~~~
{
  "id": "dpr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3",
  "domain": "example.com",
  "provider": "cloudflare",
  "connection_id": "dcn_01J8Z3Q4R5S6T7V8W9X0Y1Z2A4",
  "title": "Add the TXT record that proves example.com is yours",
  "why": "Your mail provider asks for it.",
  "risk": "None: a TXT record changes no traffic.",
  "status": "applied",
  "changes": [
    {
      "op": "update",
      "record_id": "372e67954025e0ba6aaa6d586b9e0b59",
      "before": {
        "type": "A",
        "name": "www.example.com",
        "content": "192.0.2.1",
        "ttl": 1,
        "proxied": true,
        "priority": null,
        "comment": null
      },
      "after": {
        "type": "A",
        "name": "www.example.com",
        "content": "192.0.2.7",
        "ttl": 1,
        "proxied": true,
        "priority": null,
        "comment": null
      }
    }
  ],
  "created_by": "user:usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
  "created_at": "2026-10-28T18:00:00.000Z",
  "decided_at": "2026-10-28T18:01:59.000Z",
  "applied_at": "2026-10-28T18:02:00.000Z",
  "rolled_back_at": null,
  "expires_at": "2026-11-04T18:00:00.000Z",
  "error": null,
  "href": "https://openvibe.domains/panel/proposals/dpr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3"
}
~~~~

Rejected: bad-op

~~~~
{
  "id": "dpr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3",
  "domain": "example.com",
  "provider": "cloudflare",
  "connection_id": "dcn_01J8Z3Q4R5S6T7V8W9X0Y1Z2A4",
  "title": "Add the TXT record that proves example.com is yours",
  "why": "Your mail provider asks for it.",
  "risk": "None: a TXT record changes no traffic.",
  "status": "pending",
  "changes": [
    {
      "op": "replace_all",
      "record_id": null,
      "before": null,
      "after": null
    }
  ],
  "created_by": "user:usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
  "created_at": "2026-10-28T18:00:00.000Z",
  "decided_at": null,
  "applied_at": null,
  "rolled_back_at": null,
  "expires_at": "2026-11-04T18:00:00.000Z",
  "error": null,
  "href": "https://openvibe.domains/panel/proposals/dpr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3"
}
~~~~

Rejected: token-in-proposal

~~~~
{
  "id": "dpr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3",
  "domain": "example.com",
  "provider": "cloudflare",
  "connection_id": "dcn_01J8Z3Q4R5S6T7V8W9X0Y1Z2A4",
  "title": "Add the TXT record that proves example.com is yours",
  "why": "Your mail provider asks for it.",
  "risk": "None: a TXT record changes no traffic.",
  "status": "pending",
  "changes": [
    {
      "op": "create",
      "record_id": null,
      "before": null,
      "after": {
        "type": "TXT",
        "name": "example.com",
        "content": "proof=abc123",
        "ttl": 1,
        "proxied": null,
        "priority": null,
        "comment": null
      }
    }
  ],
  "created_by": "user:usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A5",
  "created_at": "2026-10-28T18:00:00.000Z",
  "decided_at": null,
  "applied_at": null,
  "rolled_back_at": null,
  "expires_at": "2026-11-04T18:00:00.000Z",
  "error": null,
  "href": "https://openvibe.domains/panel/proposals/dpr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3",
  "api_token": "example-not-a-secret"
}
~~~~

## Validate

~~~~
const contracts = require('openvibe-contracts');
contracts.validate('domains.proposal@1', value);   // { valid, errors: [{ path, message }] }
~~~~
