Skip to content
Draft
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
25 changes: 25 additions & 0 deletions .github/workflows/test_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,32 @@
- uses: ./.github/actions/setup_node_environment
- run: pnpm test

openapi-sync-check:
name: "OpenAPI Spec Sync Check"
runs-on: blacksmith-4vcpu-ubuntu-2204
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup_node_environment

- name: Generate OpenAPI spec from production
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is the test_ci.yml workflow, I don't believe we should be checking against production here.

My understanding is that the following will be required:

  • Checkout the repo (from the same branch)
  • Create a build of ENSApi (from the same branch)
  • Execute the build of ENSApi such that we can know its hostname / port
  • Send the request to openapi:generate to the hostname / port described above.

run: pnpm --filter ensapi openapi:generate

- name: Format generated spec with Biome
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see related comments about moving the responsibility for formatting into the route handler.

run: pnpm biome format --write docs/docs.ensnode.io/openapi.json

- name: Verify OpenAPI spec matches production
run: |
if git diff --exit-code docs/docs.ensnode.io/openapi.json; then
echo "✅ OpenAPI spec is in sync with production"
else
echo "❌ OpenAPI spec is out of sync with production"
echo ""
echo "The committed openapi.json differs from the production API."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an easy way to output here specifically what was different in the git diff?

echo "Run 'pnpm --filter ensapi openapi:generate' and commit the changes."
exit 1
fi

integrity-check:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium test

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
name: "Integrity Check"
runs-on: blacksmith-4vcpu-ubuntu-2204
services:
Expand Down
3 changes: 2 additions & 1 deletion apps/ensapi/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"test": "vitest",
"lint": "biome check --write .",
"lint:ci": "biome ci",
"typecheck": "tsc --noEmit"
"typecheck": "tsc --noEmit",
"openapi:generate": "tsx scripts/generate-openapi.ts"
},
"dependencies": {
"@ensdomains/ensjs": "^4.0.2",
Expand Down
51 changes: 51 additions & 0 deletions apps/ensapi/scripts/generate-openapi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env tsx

/**
* Generate OpenAPI spec from a running ENSApi instance.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this script should live in the Mintlify app, rather than in ENSApi?

Suggesting this because it writes to a file in the Mintlify app directory. The param it takes is a hostname/port and that could be from inside or outside the repo.

*
* Usage:
* pnpm openapi:generate # Uses default URL (production)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concerned about having any default URL for this script.

I'm not aware of any cases where we would want to regularly run this script against production. That would violate the concept of where the openapi.json file in this repo is supposed to come from.

Instead, I believe this script should not have any default for this param and always require this param to be explicitly passed. Otherwise it is too error prone.

* pnpm openapi:generate http://localhost:3223 # Uses custom URL
* ENSAPI_URL=http://localhost:3223 pnpm openapi:generate
*
* Output:
* Writes openapi.json to the docs directory for Mintlify to consume.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Writes openapi.json to the docs directory for Mintlify to consume.
* Writes openapi.json to the docs directory for Mintlify to consume. Note that a rebuild of Mintlify is required for it to reflect an updated openapi.json.

Is that fair?

* Run `pnpm biome format --write docs/docs.ensnode.io/openapi.json` after to format.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bummer that this is necessary as an additional step.

What do you think about a strategy where we update the logic for the /openapi.json route handler in ENSApi so that it:

  1. Doesn't directly return whatever the output is from openAPIRouteHandler. Instead, before returning it, it performs formatting.
  2. Implements some logic so that it only generates the OpenAPI spec once in the lifetime of an ENSApi instance and then after that stores that response in memory as a string it can just return with 0 additional processing.

*/

import { writeFileSync } from "node:fs";
import { resolve } from "node:path";

const DEFAULT_ENSAPI_URL = "https://api.alpha.ensnode.io";
const OUTPUT_PATH = resolve(import.meta.dirname, "../../../docs/docs.ensnode.io/openapi.json");

async function main() {
// Get URL from argument or environment variable
const ensapiUrl = process.argv[2] || process.env.ENSAPI_URL || DEFAULT_ENSAPI_URL;
const openapiUrl = `${ensapiUrl}/openapi.json`;
Comment on lines +24 to +25
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Handle potential trailing slash in URL.

If ensapiUrl ends with a /, the constructed URL will have //openapi.json. Consider normalizing:

Proposed fix
  const ensapiUrl = process.argv[2] || process.env.ENSAPI_URL || DEFAULT_ENSAPI_URL;
- const openapiUrl = `${ensapiUrl}/openapi.json`;
+ const openapiUrl = `${ensapiUrl.replace(/\/+$/, '')}/openapi.json`;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const ensapiUrl = process.argv[2] || process.env.ENSAPI_URL || DEFAULT_ENSAPI_URL;
const openapiUrl = `${ensapiUrl}/openapi.json`;
const ensapiUrl = process.argv[2] || process.env.ENSAPI_URL || DEFAULT_ENSAPI_URL;
const openapiUrl = `${ensapiUrl.replace(/\/+$/, '')}/openapi.json`;
🤖 Prompt for AI Agents
In `@apps/ensapi/scripts/generate-openapi.ts` around lines 23 - 24, ensapiUrl may
end with a trailing slash causing openapiUrl to become "//openapi.json";
normalize ensapiUrl before composing openapiUrl (the variables to change are
ensapiUrl and openapiUrl in generate-openapi.ts) by trimming any trailing '/'
from ensapiUrl (or using a URL-safe join) so openapiUrl is built as
`${normalizedEnsapiUrl}/openapi.json` even when ENSAPI_URL or process.argv[2]
includes a trailing slash; ensure DEFAULT_ENSAPI_URL remains fallback and
normalization runs after selecting the value.


console.log(`Fetching OpenAPI spec from: ${openapiUrl}`);

const response = await fetch(openapiUrl);

if (!response.ok) {
console.error(`Failed to fetch OpenAPI spec: ${response.status} ${response.statusText}`);
process.exit(1);
}
Comment on lines +29 to +34
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script lacks error handling for network timeouts or when the URL is unreachable. Consider adding a timeout to the fetch call and providing more helpful error messages for common failure scenarios (e.g., connection refused, timeout, invalid JSON response).

Copilot uses AI. Check for mistakes.
Comment on lines +29 to +34
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider adding a timeout to the fetch request.

The fetch call has no timeout, which could cause the CI job to hang indefinitely if the production API is unresponsive. Node.js fetch supports AbortSignal.timeout():

Proposed fix
- const response = await fetch(openapiUrl);
+ const response = await fetch(openapiUrl, {
+   signal: AbortSignal.timeout(30_000), // 30 second timeout
+ });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const response = await fetch(openapiUrl);
if (!response.ok) {
console.error(`Failed to fetch OpenAPI spec: ${response.status} ${response.statusText}`);
process.exit(1);
}
const response = await fetch(openapiUrl, {
signal: AbortSignal.timeout(30_000), // 30 second timeout
});
if (!response.ok) {
console.error(`Failed to fetch OpenAPI spec: ${response.status} ${response.statusText}`);
process.exit(1);
}
🤖 Prompt for AI Agents
In `@apps/ensapi/scripts/generate-openapi.ts` around lines 28 - 33, Add a timeout
to the fetch in generate-openapi.ts by creating an AbortSignal via
AbortSignal.timeout(ms) and passing it as the signal option to the
fetch(openapiUrl) call; handle the abort case by catching the thrown error
(check for AbortError or error.name === 'AbortError') and log a clear timeout
message before exiting, and ensure the existing non-ok response handling remains
unchanged.


const spec = await response.json();

// Pretty-print the JSON for readability in git diffs
const content = `${JSON.stringify(spec, null, 2)}\n`;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see my other comment that suggested we might apply formatting within the API route handler such that it's not necessary to perform additional formatting of the response.


writeFileSync(OUTPUT_PATH, content, "utf-8");

console.log(`OpenAPI spec written to: ${OUTPUT_PATH}`);
console.log(`Spec version: ${spec.info?.version}`);
console.log(`Paths: ${Object.keys(spec.paths || {}).length}`);
}

main().catch((error) => {
console.error("Error generating OpenAPI spec:", error);
process.exit(1);
});
60 changes: 42 additions & 18 deletions docs/docs.ensnode.io/README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,58 @@
# ENSNode Documentation
# ENSNode API Documentation
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we should revert this change. It's true that at the moment the Mintlify site is only being used for the ENSApi docs. However, as soon as we can get there, we want to fully transition all ENSNode docs into Mintlify, not just the subset for ENSApi.


[docs.ensnode.io](https://docs.ensnode.io) runs on [Mintlify](https://mintlify.com).
[docs.ensnode.io](https://docs.ensnode.io) hosts the ENSApi reference documentation.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we should revert this change. It's true that at the moment the Mintlify site is only being used for the ENSApi docs. However, as soon as we can get there, we want to fully transition all ENSNode docs into Mintlify, not just the subset for ENSApi.


## Local Development
Learn more about [ENSNode](https://ensnode.io) from [the ENSNode docs](https://ensnode.io/docs/).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Learn more about [ENSNode](https://ensnode.io) from [the ENSNode docs](https://ensnode.io/docs/).
Learn more about [ENSNode](https://ensnode.io) from [the "Starlight" ENSNode docs](https://ensnode.io/docs/). Everything from these "Starlight" docs is planned to be transitioned into these Mintlify docs soon.


### Getting Started
## Architecture
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
## Architecture
## API Docs


The documentation serves API reference from two sources:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The documentation serves API reference from two sources:
This Mintlify site serves two (potentially distinct) sets of API docs from two sources:


| Section | Source | Purpose |
| -------------------- | ------------------ | ------------------------------------------- |
| **API Reference** | Production API URL | Always reflects the live deployed API |
| **Preview** (hidden) | `./openapi.json` | PR preview deployments for upcoming changes |

When you change API routes or schemas, update the committed `openapi.json` to preview changes in Mintlify's PR deployments.

## OpenAPI Spec Management

1. Clone the repository:
We use a combination of runtime URLs and committed files to keep API docs in sync across environments. This setup achieves:

```bash
git clone https://github.com/namehash/ensnode.git
```
- Production API docs match the production deployment, even when production lags behind `main`
- Non-API docs stay in sync with `main` through normal Git flow
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest ideas about non-API docs move out of a section that's dedicated to API docs.

- Each branch has its own `openapi.json`, validated by CI
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Each branch has its own `openapi.json`, validated by CI
- Each branch has its own `openapi.json`, validated by the CI to be in sync with the `openapi.json` that would actually be returned by the code for ENSApi in the same branch.

- PR previews show upcoming API changes before merge

2. Navigate to the docs directory:
### Generating the Spec

```bash
cd ensnode/docs/docs.ensnode.io
```
```bash
# Generate from production API
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest to also include other example here for generating from the code for ENSApi in your local dev environment (on your current branch)

pnpm --filter ensapi openapi:generate
```

3. Start the local development server:
### CI Validation

```bash
pnpm mint dev
```
CI runs an `openapi-sync-check` job that compares the committed `openapi.json` against production. If they differ, the check fails.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be comparing against production.


Update the committed spec when:

- You've changed API routes or schemas (generate from your local instance)
- Production was updated (generate from production)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't commit the spec from production.


## Local Development

### Getting Started

1. `git clone https://github.com/namehash/ensnode.git`
2. `cd docs/docs.ensnode.io`
3. `pnpm mint dev`
4. Open [http://localhost:3000](http://localhost:3000) in your browser

### Troubleshooting

- If a page loads as a 404, make sure you are running in a folder with a valid `docs.json`.
- Run `pnpm mint --help` to read more details about Mintlify CLI.
- If a page loads as a 404, ensure you're running in a folder with a valid `docs.json`
- Run `pnpm mint --help` for more Mintlify CLI details

## Publishing Changes

Expand All @@ -38,3 +61,4 @@ Changes pushed to the main branch are automatically deployed to production.
## Resources

- [Mintlify documentation](https://mintlify.com/docs)
- [ENSNode documentation](https://ensnode.io/docs/)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- [ENSNode documentation](https://ensnode.io/docs/)
- [ENSNode "Starlight" docs](https://ensnode.io/docs/)

8 changes: 7 additions & 1 deletion docs/docs.ensnode.io/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,13 @@
},
{
"group": "API Reference",
"openapi": "https://gist.githubusercontent.com/notrab/94b637e77468cbddd895d7933ce88f64/raw/12cb5ed183558a9bdda5d1c7004db6c794dbd13e/green-ensnode-openapi.json"
"openapi": "https://api.alpha.ensnode.io/openapi.json"
},
{
"group": "Preview",
"pages": ["ensapi/preview"],
"openapi": "./openapi.json",
"hidden": true
}
]
Comment on lines +32 to 35
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "hidden" property may not be a valid field in the Mintlify docs.json schema. According to Mintlify documentation, there is no "hidden" property for navigation groups. If you want to hide this section, you should verify this property is supported or use a different approach.

Suggested change
"openapi": "./openapi.json",
"hidden": true
}
]
"openapi": "./openapi.json"
}
]
]

Copilot uses AI. Check for mistakes.
}
Expand Down
9 changes: 9 additions & 0 deletions docs/docs.ensnode.io/ensapi/preview.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
title: API Preview
sidebarTitle: Preview
description: Preview upcoming API changes from the current branch.
---

This page shows the OpenAPI specification from the current branch, which may include unreleased API changes.

For the production API documentation, see the [API Reference](/ensapi).
Loading