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
58 changes: 58 additions & 0 deletions .github/workflows/docs-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Documentation CI

on:
push:
branches: [main, dev]
paths:
- 'docs/site/**'
- '.github/workflows/docs-ci.yml'
pull_request:
branches: [main, dev]
paths:
- 'docs/site/**'
- '.github/workflows/docs-ci.yml'

jobs:
validate-docs:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4

- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 20
cache: 'npm'
cache-dependency-path: 'sdk/typescript/package-lock.json'

# The examples import @trident-indexer/sdk via a file: dependency, which
# resolves to the SDK's built dist/ — and dist/ is gitignored, so without
# this step tsc fails with "Cannot find module '@trident-indexer/sdk'".
# Same reason the sdk-react job in ci.yml builds the TypeScript SDK
# before installing.
- name: Build the TypeScript SDK
working-directory: sdk/typescript
run: npm ci && npm run build

- name: Validate TypeScript Code Examples
run: |
cd docs/site
npm install --no-save typescript @trident-indexer/sdk@file:../../sdk/typescript
npx tsc --noEmit

# Only the .mdx files are checked. The previous 'docs/site/**/*.json' glob
# matched no tracked file in this repo — `git ls-files 'docs/site/**/*.json'`
# returns nothing — so in practice it only ever reached the dependency
# metadata npm unpacks into docs/site/node_modules during the step above,
# and failed the job on other people's rotted author URLs and on
# placeholder links inside TypeScript's localised diagnostic files.
#
# Remaining exclusions (unpublished product pages) live in .lycheeignore
# at the repo root, which lychee reads automatically.
- name: Check for Broken Links
uses: lycheeverse/lychee-action@2b973e86fc7b1f6b36a93795fe2c9c6ae1118621 # v1
with:
args: --verbose --no-progress 'docs/site/**/*.mdx'
fail: true
23 changes: 23 additions & 0 deletions .lycheeignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# URLs the docs link checker must not fail on.
# One regex per line, matched against the URL. lychee reads this file
# automatically from the repo root.
#
# Note these are URL patterns, not paths — a bare directory name here matches
# nothing. Files are excluded by narrowing the globs in
# .github/workflows/docs-ci.yml instead.

# Product pages that are not published yet. The marketing site resolves, but
# these paths do not exist, so the checker fails on links the docs are correct
# to contain — they are where a reader should go once the pages ship. Remove
# each line as the corresponding page goes live.
https://trident\.telocel\.com/signup
https://trident\.telocel\.com/pricing

# The API root serves no HTML document; only the versioned endpoints beneath it
# do, and those are covered by the contract tests rather than a link check.
https://api\.trident\.telocel\.com/?$

# GitHub Discussions is not enabled for this repository (has_discussions is
# false), so this 404s. Delete this line if Discussions is turned on, or drop
# the link from the docs.
https://github\.com/Telocel-Labs/Trident/discussions
116 changes: 116 additions & 0 deletions docs/site/api-reference/authentication.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
---
title: "Authentication"
description: "How API key authentication works in Trident — the X-API-Key header, 401 vs 403 responses, and managing keys in self-hosted deployments."
---

Every Trident API endpoint (except `GET /v1/health`) requires authentication via an API key.

---

## The X-API-Key header

Pass your API key as the `X-API-Key` header on every request:

```bash
curl https://api.trident.telocel.com/v1/events \
-H "X-API-Key: tdk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```

```typescript
import { TridentClient } from "@trident-indexer/sdk";

const client = new TridentClient({
apiUrl: "https://api.trident.telocel.com",
apiKey: process.env.TRIDENT_API_KEY!,
network: "testnet",
});
// The SDK sends X-API-Key automatically on every request.
```

<Warning>
Never expose your API key in client-side code, public repositories, or browser environments. Always read it from an environment variable or a secrets manager.
</Warning>

---

## Getting an API key

### Hosted API

Sign up at [trident.telocel.com](https://trident.telocel.com/signup). Free tier keys are issued immediately. Each key is tied to a rate-limit tier (see [Rate Limiting](/api-reference/rate-limiting)).

### Self-hosted

API keys in a self-hosted deployment are managed via environment variables:

1. **Generate a salt** (do this once per deployment):
```bash
openssl rand -hex 32
# → a3f7c2b1...
```

2. **Set the salt in `.env`**:
```bash
API_KEY_SALT=a3f7c2b1...
```

3. **Hash your API keys** using HMAC-SHA256 with the salt:
```bash
echo -n "your-raw-api-key" | openssl dgst -sha256 -hmac "a3f7c2b1..."
# → SHA2-256(stdin)= 9c1d3b...
```

4. **Set the hashes in `.env`**:
```bash
API_KEY_HASHES=9c1d3b...,another-hash-here
```

The API compares incoming `X-API-Key` values (HMAC-hashed with `API_KEY_SALT`) against the list in `API_KEY_HASHES` using a constant-time comparison.

---

## Error responses

### 401 Unauthorized

Returned when the `X-API-Key` header is missing or the key value is not recognised.

```json
{
"error": {
"code": "UNAUTHORIZED",
"message": "missing or invalid API key"
}
}
```

**Causes:**
- Header omitted entirely
- Key value is malformed (not the correct length/format)
- Key has been revoked

### 403 Forbidden

Returned when the key is valid but does not have permission to access the requested resource or tier.

```json
{
"error": {
"code": "FORBIDDEN",
"message": "this endpoint requires a Pro tier key"
}
}
```

**Causes:**
- Free-tier key attempting to access a Pro-only endpoint
- Key's account is suspended

---

## Security recommendations

- Store API keys in environment variables, never in source code
- Rotate keys regularly (issue a new key, update deployments, revoke the old key)
- Use different keys for development and production environments
- In self-hosted mode, protect `API_KEY_SALT` with the same care as a database password — changing it invalidates all existing `API_KEY_HASHES`
115 changes: 115 additions & 0 deletions docs/site/api-reference/events-get.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
---
title: "GET /v1/events/:id"
description: "Retrieve a single Soroban event by its UUID."
---

## Endpoint

```
GET /v1/events/:id
```

Retrieve a specific indexed Soroban event by its UUID.

---

## Authentication

Requires `X-API-Key` header. See [Authentication](/api-reference/authentication).

---

## Path parameters

| Parameter | Type | Required | Description |
|-----------|----------|----------|------------------------------------|
| `id` | `string` | ✅ | UUID v4 of the event to retrieve. |

The `id` must be a valid UUID v4 (e.g. `550e8400-e29b-41d4-a716-446655440000`). Malformed UUIDs return `400 INVALID_ARGUMENT`.

---

## Example request

```bash
curl "https://api.trident.telocel.com/v1/events/550e8400-e29b-41d4-a716-446655440000" \
-H "X-API-Key: $TRIDENT_API_KEY"
```

---

## Response

### 200 OK

```json
{
"event": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"contractId": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
"ledgerSequence": 12345678,
"ledgerTimestamp": "2025-01-15T10:30:00Z",
"transactionHash": "3389e9f0f1a65f19935ef8b398905b88e74e9b0bb72a13dea36dc0cfd4ab2bd1",
"eventIndex": 0,
"eventType": "contract",
"topics": [
"AAAADQAAAAh0cmFuc2Zlcg==",
"AAAAE..."
],
"data": null,
"createdAt": "2025-01-15T10:30:01Z"
}
}
```

---

## Error responses

| Status | Code | When |
|--------|--------------------|------------------------------------------------|
| `400` | `INVALID_ARGUMENT` | `id` is not a valid UUID v4. |
| `401` | `UNAUTHORIZED` | Missing or invalid `X-API-Key`. |
| `404` | `NOT_FOUND` | No event with this UUID exists in the index. |
| `429` | `RATE_LIMITED` | Rate limit exceeded. |
| `503` | `UNAVAILABLE` | Service temporarily unavailable. |

### 404 Not Found

```json
{
"error": {
"code": "NOT_FOUND",
"message": "event not found"
}
}
```

---

## SDK equivalent

```typescript
import { TridentClient, TridentError } from "@trident-indexer/sdk";
import type { SorobanEvent } from "@trident-indexer/sdk";

const client = new TridentClient({
apiUrl: "https://api.trident.telocel.com",
apiKey: process.env.TRIDENT_API_KEY!,
network: "testnet",
});

try {
const event: SorobanEvent = await client.getEventById({
id: "550e8400-e29b-41d4-a716-446655440000",
});
console.log("Ledger:", event.ledgerSequence);
console.log("Transaction:", event.transactionHash);
} catch (err) {
if (err instanceof TridentError && err.code === "NOT_FOUND") {
console.error("Event does not exist in the index");
} else {
throw err;
}
}
```
Loading
Loading