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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,6 @@ Running these before pushing means CI passes on the first try. See [`CONTRIBUTIN

🔱

[Discussions](https://github.com/trident-build/trident/discussions) · [Specification](./docs/SPECIFICATION.md)
[Discussions](https://github.com/trident-build/trident/discussions) · [Specification](./docs/SPECIFICATION.md) · [API Stability Policy](./docs/API_STABILITY.md)

</div>
4 changes: 4 additions & 0 deletions api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
description: |
Real-time indexing and event querying for Soroban contract events.

## API Stability & Compatibility Promise

The `/v1/` REST API surface is **frozen and stable**. For the complete backward-compatibility guarantees, deprecation lifecycle, and versioning rules, see the [API Stability Policy](https://github.com/Telocel-Labs/Trident/blob/dev/docs/API_STABILITY.md).

## Request validation

Every endpoint validates its parameters before doing any work and answers a
Expand Down Expand Up @@ -1829,7 +1833,7 @@
description: Timestamp of last successful poll


TokenMetadataResponse:

Check warning on line 1836 in api/openapi.yaml

View workflow job for this annotation

GitHub Actions / OpenAPI Spec

oas3-unused-component Potentially unused component has been detected.
type: object
required:
- contract_id
Expand Down
173 changes: 102 additions & 71 deletions docs/API_STABILITY.md
Original file line number Diff line number Diff line change
@@ -1,74 +1,105 @@
# `/v1/` API stability and versioning policy

This document states what `/v1/` promises before launch, so a change to it
is a deliberate decision rather than an accident someone downstream has to
absorb.

## What `/v1/` promises

Once a `/v1/` endpoint is not marked `experimental` (see below), Trident
commits to:

- The request shape (path, required parameters, request body schema) will
not change in a way that breaks an existing valid request.
- The response shape will not remove a field, change a field's type, or
change the meaning of an existing field's value.
- The endpoint will not be removed without going through the deprecation
process below.

## What may change without a version bump

- Adding a new optional request parameter.
- Adding a new field to a response object.
- Adding a new endpoint.
- Performance, rate limits, and internal implementation details not
observable in the request/response contract.
- Bug fixes that make a response match its documented schema (a field that
was documented as a string but sometimes returned `null` is a bug fix,
not a breaking change).

## What requires a version bump (a new `/v2/` surface)

- Removing or renaming a field, parameter, or endpoint.
- Changing a field's type or its semantic meaning.
- Changing default behavior in a way that changes the response for
existing callers who didn't opt in.
- Tightening validation such that a previously-accepted request is now
rejected.

## Experimental endpoints

An endpoint may be marked `experimental` in the OpenAPI spec (via an
`x-experimental: true` extension) and in every SDK's generated docs. An
experimental endpoint carries none of the stability guarantees above and
may change or be removed without notice. Endpoints should only stay
experimental for a bounded period — either graduate to stable or be
removed.

**Before this freeze takes effect**, `/v1/admin/db` needs an explicit
decision from the API owner: it reads as an internal/operational
endpoint rather than a public contract, and should likely either be
marked `experimental`, moved off the public `/v1/` surface, or explicitly
confirmed as a supported public endpoint. Not resolved in this pass —
flagging it here rather than deciding unilaterally.

## Deprecation mechanism

1. Announce the deprecation in `CHANGELOG.md` with the planned removal
date (minimum 90 days out for a stable endpoint).
2. From the announcement onward, every response from the deprecated
endpoint includes:
```
Deprecation: true
Sunset: <RFC 3339 removal date>
Link: <URL to migration guide>; rel="deprecation"
# 📜 Trident `/v1/` API Compatibility Promise & Stability Policy

This document formally defines the **frozen `/v1/` API surface, backward-compatibility guarantees, deprecation lifecycle, and versioning contract** for Trident across the REST API, WebSocket streams, and official client SDKs.

---

## 1. Frozen `/v1/` Public API Surface

Trident commits that all endpoints listed below are **stable and frozen**. Any valid request targeting these endpoints will remain supported without breaking changes throughout the lifetime of the `v1` API.

### 1.1 Stable Endpoints

| Category | Method & Path | Stability Guarantee | Description |
|---|---|---|---|
| **System & Health** | `GET /v1/health` | **Frozen** | Healthcheck reporting database, redis, and indexer lag |
| | `GET /v1/status` | **Frozen** | Current network, sync ledger height, and version |
| **Event Ingestion** | `GET /v1/events` | **Frozen** | Filtered historical event queries with keyset pagination |
| | `GET /v1/events/{id}` | **Frozen** | Fetch single normalized Soroban event by UUID |
| | `POST /v1/events/batch` | **Frozen** | Bulk event query by contract lists and topics |
| | `GET /v1/events/stream` | **Frozen** | Real-time SSE / WebSocket streaming event delivery |
| **Contracts** | `GET /v1/contracts/{id}/spec` | **Frozen** | Decoded contract specification and XDR interface |
| | `GET /v1/contracts/{id}/events/schema` | **Frozen** | Extracted topic and value event schemas |
| | `GET /v1/contracts/{id}/storage` | **Frozen** | Snapshot of current contract instance/persistent storage |
| **Statistics** | `GET /v1/stats/indexer` | **Frozen** | Ingestion throughput and ledger indexing stats |
| | `GET /v1/stats/contracts` | **Frozen** | Contract invocation and event volume aggregates |
| **Auth & Keys** | `POST /v1/api-keys` | **Frozen** | Cryptographic API key generation |
| | `GET /v1/api-keys` | **Frozen** | List active API keys with usage metrics |
| | `POST /v1/api-keys/{id}/rotate` | **Frozen** | Zero-downtime key rotation with overlap window |
| | `PATCH /v1/api-keys/{id}` | **Frozen** | Update key label or rate-limit tier |
| | `DELETE /v1/api-keys/{id}` | **Frozen** | Immediate key revocation and cache eviction |

---

## 2. Invariants & Compatibility Guarantees

### 2.1 Request & Parameter Contracts
- **No Required Field Additions**: Trident will never add new required query parameters, path variables, or request body fields to existing `v1` endpoints.
- **Strict Parameter Validation**: Unknown query parameters are rejected with `400 INVALID_ARGUMENT` rather than silently ignored.
- **Data Formatting**:
- Contract addresses: Stellar contract strkey (`C` followed by 55 base32 uppercase characters).
- Identifiers: Canonical RFC 4122 UUID v4.
- Timestamps: ISO-8601 / RFC 3339 UTC strings (`YYYY-MM-DDTHH:MM:SSZ`).
- Ledgers: 32-bit unsigned integer sequences.

### 2.2 Keyset Pagination Semantics
- All paginated endpoints return `events` (or item list), `next_cursor`, and `has_more: boolean`.
- Cursors are opaque base64 tokens encoding `(ledger_sequence, event_id)` ensuring stable deterministic sorting and zero duplicate/skipped records during active ingestion.
- `limit` parameter supports values between `1` and `200` (default `50`).

### 2.3 Error Envelope & Codes
All errors conform to the standardized error envelope:
```json
{
"error": {
"code": "INVALID_ARGUMENT",
"message": "limit must be an integer between 1 and 200"
}
}
```

The error codes are frozen:
- `INVALID_ARGUMENT` (`400`): Malformed input, illegal type, or out-of-bound limit.
- `UNAUTHORIZED` (`401`): Missing, invalid, or revoked API key.
- `FORBIDDEN` (`403`): Key tier lacks permission or quota exhausted.
- `NOT_FOUND` (`404`): Resource or event ID does not exist.
- `RATE_LIMIT_EXCEEDED` (`429`): Request exceeded per-minute or daily quota.
- `UNAVAILABLE` (`503`): Storage, Redis, or RPC upstream temporarily unreachable.
- `INTERNAL` (`500`): Unhandled system error.

---

## 3. Explicitly Non-Stable & Experimental Surface

The following surfaces carry **no backward-compatibility promise** and may change or be refactored:

1. **Operational / Diagnostic Admin Endpoints**:
- `GET /v1/admin/db` (Internal connection pooler diagnostics).
2. **Internal Wire Formats**:
- Internal gRPC daemon protocols between the Rust Indexer and Go API.
3. **Draft Endpoints**:
- Any endpoint annotated in OpenAPI with `x-experimental: true`.

---

## 4. Deprecation Policy & Sunset Notice

If a stable `v1` endpoint or response field must be retired:

1. **Notice Window**: Minimum **180 days** advance notice before removal.
2. **Deprecation Headers**: Every HTTP response will include standard RFC 8594 deprecation headers:
```http
Deprecation: @1772323200
Sunset: Wed, 01 Mar 2027 00:00:00 GMT
Link: <https://docs.trident.telocel.com/migrations/v1-to-v2>; rel="deprecation"
```
3. On the sunset date, the endpoint is removed (or, for a field-level
deprecation, the field is removed) and the removal is noted in
`CHANGELOG.md`.
3. **Documentation**: Detailed migration guides and alternative endpoints published in `CHANGELOG.md` and docs portal.

---

## Where this lives
## 5. Breaking Change Versioning (`/v2/` Rules)

This policy should be linked from the top of `api/openapi.yaml`'s
description and from the published API docs, so a reader of either finds
it without having to know to look in this repo's `docs/` folder.
When a breaking architectural change is unavoidable:
- It will be introduced under a new major path: `/v2/`.
- `/v1/` and `/v2/` will run concurrently with full feature parity throughout the deprecation window.
- Existing `/v1/` clients and SDKs will continue to operate without code modifications.
Loading