Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,16 @@ jobs:
restore-keys: ${{ runner.os }}-cargo-

- name: Check
run: cargo check
run: cargo check --workspace

- name: Format
run: cargo fmt --check

- name: Lint
run: cargo clippy --all-targets -- -D warnings
run: cargo clippy --workspace --all-targets -- -D warnings

- name: Test
run: cargo test
run: cargo test --workspace

- name: Smoke test
run: ./scripts/smoke-test.sh
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/rust-port-alpha.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ jobs:
restore-keys: ${{ runner.os }}-cargo-test-

- name: Clippy
run: cargo clippy --all-targets -- -D warnings
run: cargo clippy --workspace --all-targets -- -D warnings

- name: Test
run: cargo test
run: cargo test --workspace

build:
name: Build ${{ matrix.target }}
Expand Down
14 changes: 8 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,22 @@ This is a command-line application. Source code is written in Rust and lives und

- **Build**: `cargo build`
- **Run**: `cargo run -- <command>`
- **Test**: `cargo test`
- **Lint**: `cargo clippy -- -D warnings`
- **Test**: `cargo test --workspace` (the workspace has a root package, so plain
`cargo test` silently skips the `domains-client` and `generate-api-catalog`
members — always pass `--workspace`)
- **Lint**: `cargo clippy --workspace -- -D warnings`
- **Format**: `cargo fmt`
- **Check**: `cargo check`
- **Check**: `cargo check --workspace`
- **Refresh API specs**: `cargo run -p generate-api-catalog`
— set `SKIP_DOMAINS_REFRESH`/`SKIP_HOSTING_REFRESH` to skip either pull for
local iteration without network access; `HOSTING_SPEC_URL`/`HOSTING_SPEC_PATH`
override where the hosting-nodejs spec comes from.

## Verification Checklist (required before finishing work)

- `cargo check` — must pass
- `cargo clippy -- -D warnings` — must pass with zero warnings
- `cargo test` — must pass
- `cargo check --workspace` — must pass
- `cargo clippy --workspace -- -D warnings` — must pass with zero warnings
- `cargo test --workspace` — must pass
- `cargo fmt --check` — must be clean
- `./rust/scripts/check-module-size.sh` — must pass

Expand Down
85 changes: 85 additions & 0 deletions rust/domains-client/openapi/domains.oas3.json
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,16 @@
"$ref": "#/components/schemas/uuid"
}
},
"tld": {
"description": "The Top-Level Domain (e.g. com, net, io) whose registration schema should be retrieved.\n",
"example": "com",
"in": "path",
"name": "tld",
"required": true,
"schema": {
"type": "string"
}
},
"totalRequired": {
"description": "When true, the response includes totalItems and totalPages for the current filter when at least one record matches. Both are omitted when the result set is empty. Defaults to false; omitting totals avoids the cost of a count query on large collections.\n",
"example": true,
Expand Down Expand Up @@ -1580,6 +1590,13 @@
"title": "Registration Quote",
"type": "object"
},
"RegistrationSchema": {
"$id": "https://godaddy.com/schemas/domains/domain-lifecycle/registration-schema.v3",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "TLD-specific JSON Schema describing the fields required to register a domain under this TLD. The structure varies per TLD and is not further constrained at the API layer. Use the contents to guide construction of the POST /registration-quotes and POST /registrations request bodies.\n",
"title": "Registration Schema",
"type": "object"
},
"ResolvedSettings": {
"$id": "https://godaddy.com/schemas/domains/domain-lifecycle/resolved-settings.v3",
"$schema": "https://json-schema.org/draft/2020-12/schema",
Expand Down Expand Up @@ -3302,6 +3319,74 @@
]
}
},
"/v3/domains/registration-schemas/{tld}": {
"get": {
"description": "Returns the TLD-specific JSON Schema describing the fields required\nwhen calling POST /registration-quotes or POST /registrations for\nthe given TLD. The schema shape varies by TLD; clients should call\nthis endpoint before quoting or registering to discover required\ncontact and eligibility fields.\n",
"operationId": "getRegistrationSchema",
"parameters": [
{
"description": "Optional client-generated request correlation identifier, propagated across services and returned in the response X-Request-Id header.\n",
"in": "header",
"name": "X-Request-Id",
"required": false,
"schema": {
"$ref": "#/components/schemas/uuid"
}
},
{
"description": "Reseller acting on behalf of a shopper account. When present, all domain operations are scoped to the specified shopper. Absent, the authenticated entity's own account is used. Only valid for reseller OAuth tokens.\n",
"example": "shopper_123",
"in": "header",
"name": "X-Shopper-Id",
"required": false,
"schema": {
"type": "string"
}
},
{
"description": "The Top-Level Domain (e.g. com, net, io) whose registration schema should be retrieved.\n",
"example": "com",
"in": "path",
"name": "tld",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RegistrationSchema"
}
}
},
"description": "TLD-specific registration schema.",
"headers": {
"X-Request-Id": {
"description": "Request correlation identifier echoed from the request or server-generated.",
"schema": {
"$ref": "#/components/schemas/uuid"
}
}
}
}
},
"security": [
{
"oauth2": [
"domains.domain:read"
]
}
],
"summary": "Retrieve the registration schema for a TLD",
"tags": [
"Registrations"
]
}
},
"/v3/domains/registrations": {
"post": {
"description": "Executes a previously quoted domain registration. **Irreversible once\naccepted; creates a charge.** Requires a valid unexpired quoteToken from\n`quoteDomainRegistration`, an `Idempotency-Key` header, and a consent\nrecord. The target domain and period are in the request body alongside\nthe quoteToken.\n\nIdempotency takes precedence over the single-use check: retrying with\nthe same `Idempotency-Key` replays the original operation even after\nthe token is consumed.\n\nReturns a `Registration` entity. Poll `links[rel=self]`\n(`GET /registrations/{registrationId}`) until status is `COMPLETED` or\n`FAILED`. The `operationId` field is also provided for clients that\nprefer `GET /operations/{operationId}`; both resolve the same resource.\n\nPoll either until status is `COMPLETED` or `FAILED`. The operation is\nfire-and-forget; always poll at least once even if the server completed\nit synchronously.\n\nWhen `iscCode` was supplied at quote time, the same value must be\nprovided here or the request fails with `422 quote_mismatch`.\n\n**PREMIUM domains:** when the quote's `fees` array is non-empty\n(inventory `PREMIUM`), the execute request must include\n`consent.acknowledgedFees` containing the same fees verbatim —\nsame types, amounts, and currencies. This confirms the customer\nexplicitly saw and accepted the specific charge before the\nirreversible purchase is executed.\n\n- `acknowledgedFees` absent when fees exist → `422` with error\n name `consent_fees_required` (conditionally required by the quote; the\n constraint spans two requests and cannot be expressed in the\n schema).\n- `acknowledgedFees` present but type or amount does not match\n the locked quote → `422` with error name `quote_mismatch`.\n",
Expand Down
11 changes: 6 additions & 5 deletions rust/domains-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ mod tests {
.check_availability()
.body(types::AvailabilityCheckCriteria {
domains: vec!["a.com".to_string(), "b.com".to_string()],
optimize_for: types::OptimizationTarget::Speed,
optimize_for: Some(types::OptimizationTarget::Speed),
isc_code: None,
})
.send()
Expand Down Expand Up @@ -361,9 +361,9 @@ mod tests {
actor: None,
ip: Some("127.0.0.1".to_string()),
principal: "shopper-42".to_string(),
type_: types::ConsentActorType::Direct,
type_: types::ConsentActorType("DIRECT".to_string()),
}),
agreement_types: vec![types::AgreementType::ApiDpa],
agreement_types: vec![types::AgreementType("API_DPA".to_string())],
acknowledged_fees: vec![],
},
created_at: None,
Expand Down Expand Up @@ -474,6 +474,7 @@ mod tests {
data: "1.2.3.4".to_string(),
flag: None,
name: "www".to_string(),
parameters: None,
port: None,
priority: None,
protocol: None,
Expand Down Expand Up @@ -540,7 +541,7 @@ mod tests {
when.method(GET)
.path("/v1/domains/agreements")
.query_param("tlds", "com")
.query_param("privacy", "false");
.query_param("v1-privacy", "false");
then.status(200).json_body(json!([
{ "agreementKey": "DNRA", "title": "Registration Agreement", "url": "https://x" }
]));
Expand All @@ -550,7 +551,7 @@ mod tests {
let agreements = client_for(&server)
.agreements()
.tlds(vec!["com".to_string()])
.privacy(false)
.v1_privacy(false)
.send()
.await
.expect("request succeeds")
Expand Down
84 changes: 84 additions & 0 deletions rust/schemas/api/domains.json
Original file line number Diff line number Diff line change
Expand Up @@ -1284,6 +1284,13 @@
"title": "Registration Quote",
"type": "object"
},
"RegistrationSchema": {
"$id": "https://godaddy.com/schemas/domains/domain-lifecycle/registration-schema.v3",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "TLD-specific JSON Schema describing the fields required to register a domain under this TLD. The structure varies per TLD and is not further constrained at the API layer. Use the contents to guide construction of the POST /registration-quotes and POST /registrations request bodies.\n",
"title": "Registration Schema",
"type": "object"
},
"ResolvedSettings": {
"$id": "https://godaddy.com/schemas/domains/domain-lifecycle/resolved-settings.v3",
"$schema": "https://json-schema.org/draft/2020-12/schema",
Expand Down Expand Up @@ -2671,6 +2678,83 @@
],
"summary": "Quote a single-domain registration (no commitment)"
},
{
"description": "Returns the TLD-specific JSON Schema describing the fields required\nwhen calling POST /registration-quotes or POST /registrations for\nthe given TLD. The schema shape varies by TLD; clients should call\nthis endpoint before quoting or registering to discover required\ncontact and eligibility fields.\n",
"method": "GET",
"operationId": "getRegistrationSchema",
"parameters": [
{
"description": "Optional client-generated request correlation identifier, propagated across services and returned in the response X-Request-Id header.\n",
"in": "header",
"name": "X-Request-Id",
"required": false,
"schema": {
"$ref": "#/$defs/uuid"
}
},
{
"description": "Reseller acting on behalf of a shopper account. When present, all domain operations are scoped to the specified shopper. Absent, the authenticated entity's own account is used. Only valid for reseller OAuth tokens.\n",
"in": "header",
"name": "X-Shopper-Id",
"required": false,
"schema": {
"type": "string"
}
},
{
"description": "The Top-Level Domain (e.g. com, net, io) whose registration schema should be retrieved.\n",
"in": "path",
"name": "tld",
"required": true,
"schema": {
"type": "string"
}
}
],
"path": "/registration-schemas/{tld}",
"responses": {
"200": {
"description": "TLD-specific registration schema.",
"schema": {
"$ref": "#/$defs/RegistrationSchema"
}
},
"400": {
"description": "Malformed request syntax, missing required field, or invalid field type.",
"schema": {
"$ref": "#/$defs/error"
}
},
"401": {
"description": "Authentication credentials are missing or invalid.",
"schema": {
"$ref": "#/$defs/error"
}
},
"403": {
"description": "Authenticated identity is not authorized to perform this operation.",
"schema": {
"$ref": "#/$defs/error"
}
},
"404": {
"description": "The requested resource was not found.",
"schema": {
"$ref": "#/$defs/error"
}
},
"429": {
"description": "Too many requests — rate limit exceeded.",
"schema": {
"$ref": "#/$defs/error"
}
}
},
"scopes": [
"domains.domain:read"
],
"summary": "Retrieve the registration schema for a TLD"
},
{
"description": "Executes a previously quoted domain registration. **Irreversible once\naccepted; creates a charge.** Requires a valid unexpired quoteToken from\n`quoteDomainRegistration`, an `Idempotency-Key` header, and a consent\nrecord. The target domain and period are in the request body alongside\nthe quoteToken.\n\nIdempotency takes precedence over the single-use check: retrying with\nthe same `Idempotency-Key` replays the original operation even after\nthe token is consumed.\n\nReturns a `Registration` entity. Poll `links[rel=self]`\n(`GET /registrations/{registrationId}`) until status is `COMPLETED` or\n`FAILED`. The `operationId` field is also provided for clients that\nprefer `GET /operations/{operationId}`; both resolve the same resource.\n\nPoll either until status is `COMPLETED` or `FAILED`. The operation is\nfire-and-forget; always poll at least once even if the server completed\nit synchronously.\n\nWhen `iscCode` was supplied at quote time, the same value must be\nprovided here or the request fails with `422 quote_mismatch`.\n\n**PREMIUM domains:** when the quote's `fees` array is non-empty\n(inventory `PREMIUM`), the execute request must include\n`consent.acknowledgedFees` containing the same fees verbatim —\nsame types, amounts, and currencies. This confirms the customer\nexplicitly saw and accepted the specific charge before the\nirreversible purchase is executed.\n\n- `acknowledgedFees` absent when fees exist → `422` with error\n name `consent_fees_required` (conditionally required by the quote; the\n constraint spans two requests and cannot be expressed in the\n schema).\n- `acknowledgedFees` present but type or amount does not match\n the locked quote → `422` with error name `quote_mismatch`.\n",
"method": "POST",
Expand Down
2 changes: 2 additions & 0 deletions rust/schemas/api/hosting-nodejs.json
Original file line number Diff line number Diff line change
Expand Up @@ -1697,6 +1697,7 @@
"summary": "Get application logs"
},
{
"description": "**Deprecated.** This operation is not available in v2 per API design review. It remains live and feature-flagged in production but may be removed in a future v1 maintenance release. Clients should not build new integrations against this endpoint.",
"method": "POST",
"operationId": "rollbackApp",
"parameters": [
Expand Down Expand Up @@ -1812,6 +1813,7 @@
"summary": "List app secrets (metadata only)"
},
{
"description": "Superseded by `POST /v2/hosting/nodejs/apps/{appId}/sync-secrets`, which accepts the same operation shape under the `hosting.secret:write` OAuth scope and returns the same secret-metadata response. This v1 endpoint remains live.",
"method": "POST",
"operationId": "updateAppSecrets",
"parameters": [
Expand Down
4 changes: 2 additions & 2 deletions rust/schemas/api/manifest.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"generated": "2026-08-26T18:46:42.423872066+00:00",
"generated": "2026-09-01T16:02:33.125619062+00:00",
"domains": {
"bulk-operations": {
"file": "bulk-operations.json",
Expand Down Expand Up @@ -34,7 +34,7 @@
"domains": {
"file": "domains.json",
"title": "Domain Lifecycle Management API",
"endpointCount": 16
"endpointCount": 17
},
"fulfillments": {
"file": "fulfillments.json",
Expand Down
9 changes: 9 additions & 0 deletions rust/schemas/openapi/hosting-nodejs-public-v1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,10 @@ paths:
post:
operationId: updateAppSecrets
summary: Add, update, or delete app secrets
description: Superseded by `POST /v2/hosting/nodejs/apps/{appId}/sync-secrets`,
which accepts the same operation shape under the `hosting.secret:write`
OAuth scope and returns the same secret-metadata response. This v1
endpoint remains live.
tags:
- Secrets
security:
Expand Down Expand Up @@ -758,6 +762,11 @@ paths:
post:
operationId: rollbackApp
summary: Rollback to a previous deployment
deprecated: true
description: "**Deprecated.** This operation is not available in v2 per API
design review. It remains live and feature-flagged in production but may
be removed in a future v1 maintenance release. Clients should not build
new integrations against this endpoint."
tags:
- Deployments
security:
Expand Down
Loading