diff --git a/blog/2019-05-28-first-blog-post.md b/blog/2019-05-28-first-blog-post.md deleted file mode 100644 index 02f3f81..0000000 --- a/blog/2019-05-28-first-blog-post.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -slug: first-blog-post -title: First Blog Post -authors: - name: Gao Wei - title: Docusaurus Core Team - url: https://github.com/wgao19 - image_url: https://github.com/wgao19.png -tags: [hola, docusaurus] ---- - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet diff --git a/blog/2019-05-29-long-blog-post.md b/blog/2019-05-29-long-blog-post.md deleted file mode 100644 index 26ffb1b..0000000 --- a/blog/2019-05-29-long-blog-post.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -slug: long-blog-post -title: Long Blog Post -authors: endi -tags: [hello, docusaurus] ---- - -This is the summary of a very long blog post, - -Use a `` comment to limit blog post size in the list view. - - - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet diff --git a/blog/2021-08-01-mdx-blog-post.mdx b/blog/2021-08-01-mdx-blog-post.mdx deleted file mode 100644 index c04ebe3..0000000 --- a/blog/2021-08-01-mdx-blog-post.mdx +++ /dev/null @@ -1,20 +0,0 @@ ---- -slug: mdx-blog-post -title: MDX Blog Post -authors: [slorber] -tags: [docusaurus] ---- - -Blog posts support [Docusaurus Markdown features](https://docusaurus.io/docs/markdown-features), such as [MDX](https://mdxjs.com/). - -:::tip - -Use the power of React to create interactive blog posts. - -```js - -``` - - - -::: diff --git a/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg b/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg deleted file mode 100644 index 11bda09..0000000 Binary files a/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg and /dev/null differ diff --git a/blog/2021-08-26-welcome/index.md b/blog/2021-08-26-welcome/index.md deleted file mode 100644 index 9455168..0000000 --- a/blog/2021-08-26-welcome/index.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -slug: welcome -title: Welcome -authors: [slorber, yangshun] -tags: [facebook, hello, docusaurus] ---- - -[Docusaurus blogging features](https://docusaurus.io/docs/blog) are powered by the [blog plugin](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog). - -Simply add Markdown files (or folders) to the `blog` directory. - -Regular blog authors can be added to `authors.yml`. - -The blog post date can be extracted from filenames, such as: - -- `2019-05-30-welcome.md` -- `2019-05-30-welcome/index.md` - -A blog post folder can be convenient to co-locate blog post images: - -![Docusaurus Plushie](./docusaurus-plushie-banner.jpeg) - -The blog supports tags as well! - -**And if you don't want a blog**: just delete this directory, and use `blog: false` in your Docusaurus config. diff --git a/blog/authors.yml b/blog/authors.yml deleted file mode 100644 index bcb2991..0000000 --- a/blog/authors.yml +++ /dev/null @@ -1,17 +0,0 @@ -endi: - name: Endilie Yacop Sucipto - title: Maintainer of Docusaurus - url: https://github.com/endiliey - image_url: https://github.com/endiliey.png - -yangshun: - name: Yangshun Tay - title: Front End Engineer @ Facebook - url: https://github.com/yangshun - image_url: https://github.com/yangshun.png - -slorber: - name: Sébastien Lorber - title: Docusaurus maintainer - url: https://sebastienlorber.com - image_url: https://github.com/slorber.png diff --git a/docs/flow/api.md b/docs/flow/api.md new file mode 100644 index 0000000..c73297e --- /dev/null +++ b/docs/flow/api.md @@ -0,0 +1,296 @@ +--- +sidebar_position: 4 +title: Pipeline API +--- + +# Flow Pipeline API + +The Flow Pipeline API creates and manages Flow pipelines from YAML. Use it for CI/CD and infrastructure-as-code workflows when pipeline definitions should live outside the Console UI. + +:::warning Rotate exposed credentials +The example pipeline file shared during this docs update contained live PostgreSQL credentials. Rotate that database password before committing or sharing any derived examples. The examples below are sanitized. +::: + +## Base URL + +```bash +export CONSOLE="https://console.withobsrvr.com" +export API_KEY="your-team-api-key" +``` + +All requests use a Team API key from Console. + +```bash +Authorization: Api-Key $API_KEY +``` + +Do not pass API keys in URL parameters. + +## Pipeline YAML format + +The Pipeline API accepts Kubernetes-style Flow resources with `apiVersion`, `kind`, `metadata`, and `spec`. Use registry component IDs, such as `contract_event` and `contract_events_postgres`, rather than runtime adapter class names. + +```yaml +apiVersion: flow.obsrvr.com/v1 +kind: Pipeline +metadata: + name: ContractEventsToPostgres +spec: + network: testnet + startLedger: "2434280" + endLedger: "2434281" + processors: + - type: contract_event + config: + network_passphrase: Test SDF Network ; September 2015 + consumers: + - type: contract_events_postgres + config: + host: postgres.example.com + port: 5432 + connect_timeout: 30 + database: defaultdb + username: postgres + password: ${POSTGRES_PASSWORD} + sslmode: require + schema: public + table_prefix: stellar_ +``` + +Use placeholders or secret injection for credentials. Do not commit live passwords. + +## Quick start: validate and apply + +Create `contract-events-to-postgres.yaml` using the shape above. + +Validate it: + +```bash +curl -X POST \ + -H "Authorization: Api-Key $API_KEY" \ + -H "Content-Type: text/yaml" \ + --data-binary @contract-events-to-postgres.yaml \ + "$CONSOLE/api/v1/flow/pipelines/validate/" +``` + +Apply it and start processing: + +```bash +curl -X POST \ + -H "Authorization: Api-Key $API_KEY" \ + -H "Content-Type: text/yaml" \ + --data-binary @contract-events-to-postgres.yaml \ + "$CONSOLE/api/v1/flow/pipelines/apply/?auto_start=true" +``` + +`apply` is idempotent by pipeline name. It creates the pipeline if it does not exist and updates it if it does. + +## Endpoints + +### Pipelines + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/api/v1/flow/pipelines/` | List team pipelines | +| POST | `/api/v1/flow/pipelines/` | Create a new pipeline | +| GET | `/api/v1/flow/pipelines/{uuid}/` | Get pipeline details | +| PUT | `/api/v1/flow/pipelines/{uuid}/` | Update a stopped pipeline | +| DELETE | `/api/v1/flow/pipelines/{uuid}/` | Delete a pipeline | +| POST | `/api/v1/flow/pipelines/{uuid}/start/` | Start a pipeline | +| POST | `/api/v1/flow/pipelines/{uuid}/stop/` | Stop a pipeline | +| GET | `/api/v1/flow/pipelines/{uuid}/config/` | Export pipeline YAML | +| POST | `/api/v1/flow/pipelines/apply/` | Idempotent create/update by name | +| POST | `/api/v1/flow/pipelines/validate/` | Validate config without creating | + +### Registry + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/api/v1/flow/registry/processors/` | List available processors and config schemas | +| GET | `/api/v1/flow/registry/consumers/` | List available consumers and config schemas | + +### Secrets + +| Method | Endpoint | Description | +|--------|----------|-------------| +| POST | `/api/v1/secrets/` | Create or update a secret | +| GET | `/api/v1/secrets/` | List secret paths, never values | +| GET | `/api/v1/secrets/{path}/` | Check whether a secret exists | +| DELETE | `/api/v1/secrets/{path}/` | Delete a secret | + +## Spec configuration + +The API derives the ledger source adapter from the selected network and ledger range. + +| Field | Description | +|-------|-------------| +| `metadata.name` | Pipeline name. `apply` matches existing pipelines by name. | +| `spec.network` | `testnet` or `mainnet`. | +| `spec.startLedger` | First ledger to process, or `latest` / `genesis`. | +| `spec.endLedger` | Optional final ledger. Omit for continuous processing. Must be greater than `startLedger` when both are numeric. | +| `spec.processors` | Processor list using registry IDs. | +| `spec.consumers` | Consumer list using registry IDs. | + +## Processor configuration + +Processors transform source records into the shape consumers expect. + +```yaml +processors: + - type: contract_event + config: + network_passphrase: Test SDF Network ; September 2015 +``` + +Use the processor registry endpoint to discover available processor types. + +```bash +curl -H "Authorization: Api-Key $API_KEY" \ + "$CONSOLE/api/v1/flow/registry/processors/" +``` + +## Consumer configuration + +Consumers write processed records to a destination. + +```yaml +consumers: + - type: contract_events_postgres + config: + host: postgres.example.com + port: 5432 + connect_timeout: 30 + database: defaultdb + username: postgres + password: ${POSTGRES_PASSWORD} + sslmode: require + schema: public + table_prefix: stellar_ +``` + +Use the consumer registry endpoint to discover available consumer types. + +```bash +curl -H "Authorization: Api-Key $API_KEY" \ + "$CONSOLE/api/v1/flow/registry/consumers/" +``` + +## Historical backfill + +Set both `startLedger` and `endLedger` to process a bounded range. The pipeline completes after the final ledger. + +```yaml +apiVersion: flow.obsrvr.com/v1 +kind: Pipeline +metadata: + name: ContractEventBackfill +spec: + network: testnet + startLedger: "2434280" + endLedger: "2435000" + processors: + - type: contract_event + config: + network_passphrase: Test SDF Network ; September 2015 + consumers: + - type: contract_events_postgres + config: + host: postgres.example.com + port: 5432 + connect_timeout: 30 + database: defaultdb + username: postgres + password: ${POSTGRES_PASSWORD} + sslmode: require + schema: public + table_prefix: stellar_ +``` + +## Start, stop, and export + +```bash +# Start +curl -X POST -H "Authorization: Api-Key $API_KEY" \ + "$CONSOLE/api/v1/flow/pipelines/$PIPELINE_UUID/start/" + +# Stop +curl -X POST -H "Authorization: Api-Key $API_KEY" \ + "$CONSOLE/api/v1/flow/pipelines/$PIPELINE_UUID/stop/" + +# Export YAML +curl -H "Authorization: Api-Key $API_KEY" \ + "$CONSOLE/api/v1/flow/pipelines/$PIPELINE_UUID/config/" +``` + +## Responses + +Create/apply success: + +```json +{ + "pipeline_id": "47d6e2c8-8fb8-46a5-9222-932f2f9d7ac9", + "action": "created", + "status": "running", + "warnings": [], + "errors": [] +} +``` + +Validation success: + +```json +{ + "valid": true, + "errors": [], + "warnings": [], + "effective_config": { + "apiVersion": "flow.obsrvr.com/v1", + "kind": "Pipeline", + "metadata": {"name": "ContractEventsToPostgres"}, + "spec": {"...": "..."} + } +} +``` + +## Error codes + +| Code | Meaning | +|------|---------| +| 400 | Invalid YAML or invalid pipeline config | +| 402 | Active subscription required to create a pipeline | +| 403 | API key is invalid or team does not match authenticated team | +| 404 | Pipeline or secret does not exist | +| 409 | Duplicate name, running pipeline update, or ambiguous apply target | +| 500 | Server error | + +## CI/CD example + +```yaml +name: Deploy Flow pipeline +on: + push: + paths: + - pipelines/** + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Validate pipeline + run: | + curl -X POST \ + -H "Authorization: Api-Key ${{ secrets.OBSRVR_API_KEY }}" \ + -H "Content-Type: text/yaml" \ + --data-binary @pipelines/contract-events-to-postgres.yaml \ + "https://console.withobsrvr.com/api/v1/flow/pipelines/validate/" + + - name: Apply pipeline + run: | + curl -X POST \ + -H "Authorization: Api-Key ${{ secrets.OBSRVR_API_KEY }}" \ + -H "Content-Type: text/yaml" \ + --data-binary @pipelines/contract-events-to-postgres.yaml \ + "https://console.withobsrvr.com/api/v1/flow/pipelines/apply/?auto_start=true" +``` diff --git a/docs/flow/concepts/pipelines.md b/docs/flow/concepts/pipelines.md index 5ad41e2..378a057 100644 --- a/docs/flow/concepts/pipelines.md +++ b/docs/flow/concepts/pipelines.md @@ -3,285 +3,193 @@ sidebar_position: 1 title: Pipelines --- -# Understanding Pipelines +# Pipelines -Pipelines are the core abstraction in Flow, representing a complete data processing workflow from source to destination. Each pipeline defines how blockchain data flows through your system, what transformations to apply, and where to deliver the results. - -## Pipeline Architecture - -A Flow pipeline consists of three main components: +A Flow pipeline is a managed Stellar data process. It reads ledger data from a source adapter, runs one or more processors, and writes records to one or more consumers. +```text +source → processor(s) → consumer(s) ``` -┌─────────────┐ ┌──────────────┐ ┌─────────────┐ -│ Source │────▶│ Processor │────▶│ Consumer │ -│ (Network) │ │ (Transform) │ │(Destination)│ -└─────────────┘ └──────────────┘ └─────────────┘ -``` - -### 1. Source (Network) -The blockchain network providing data: -- **Stellar Mainnet**: Production network -- **Stellar Testnet**: Development network -- **Starting Point**: Latest, genesis, or specific ledger -### 2. Processor -Transforms raw blockchain data: -- Filters relevant information -- Structures data for consumption -- Can be chained for complex transformations +Use Flow when you need a custom stream or custom destination. Use [Lake](/docs/lake/overview) when you only need to query standard decoded Stellar data. -### 3. Consumer -Delivers processed data to your application: -- Databases (PostgreSQL, Redis) -- Streaming (Webhooks, Kafka) -- Storage (S3, DuckDB) +## Pipeline YAML shape -## Pipeline Lifecycle +The Pipeline API accepts Kubernetes-style Flow resources. The source adapter is derived from `spec.network` and the ledger range. -### States - -Pipelines progress through several states during their lifecycle: +```yaml +apiVersion: flow.obsrvr.com/v1 +kind: Pipeline +metadata: + name: ContractEventsToPostgres +spec: + network: testnet + startLedger: "2434280" + endLedger: "2434281" + processors: + - type: contract_event + config: + network_passphrase: Test SDF Network ; September 2015 + consumers: + - type: contract_events_postgres + config: + host: postgres.example.com + port: 5432 + connect_timeout: 30 + database: defaultdb + username: postgres + password: ${POSTGRES_PASSWORD} + sslmode: require + schema: public + table_prefix: stellar_ +``` + +## Spec + +| Field | Meaning | +|-------|---------| +| `metadata.name` | Pipeline name. `apply` matches existing pipelines by name. | +| `spec.network` | `testnet` or `mainnet`. | +| `spec.startLedger` | First ledger to process, or `latest` / `genesis`. | +| `spec.endLedger` | Optional final ledger. Omit for continuous processing. | +| `spec.processors` | Processor list using registry IDs. | +| `spec.consumers` | Consumer list using registry IDs. | + +For mainnet, use: -```mermaid -graph LR - A[Pending] --> B[Deploying] - B --> C[Running] - C --> D[Stopped] - C --> E[Failed] - C --> F[Completed] - D --> B - E --> B +```yaml +network: mainnet ``` -- **Pending**: Configuration validated, awaiting deployment -- **Deploying**: Resources being allocated and services starting -- **Running**: Actively processing data -- **Stopped**: Manually paused by user -- **Failed**: Error occurred during processing -- **Completed**: Finished processing (for historical ranges) - -### Deployment Process - -1. **Validation**: Configuration checked for errors -2. **Security**: Credentials stored in Vault -3. **Orchestration**: Services deployed via Nomad -4. **Initialization**: Processors connect to data source -5. **Processing**: Data flow begins - -## Configuration - -### Basic Configuration +For testnet, use: ```yaml -name: "payment-tracker" -network: "mainnet" -start_ledger: "latest" - -processor: - type: "payments_memo" - config: - memo_text: "REF" - min_amount: "100" - -consumer: - type: "postgres" - config: - connection_string: "postgresql://..." - batch_size: 50 +network: testnet ``` -### Advanced Configuration - -#### Multiple Processors +## Processors -Chain processors for complex logic: +Processors transform source records into records consumers can write. ```yaml processors: - - type: "contract_filter" + - type: contract_event config: - contract_ids: ["CCTOKEN..."] - - type: "contract_event" - config: {} + network_passphrase: Test SDF Network ; September 2015 ``` -#### Multiple Consumers +Use the correct network passphrase: + +| Network | Passphrase | +|---------|------------| +| mainnet | `Public Global Stellar Network ; September 2015` | +| testnet | `Test SDF Network ; September 2015` | -Send data to multiple destinations: +## Consumers + +Consumers write processed records to a destination. ```yaml consumers: - - type: "postgres" - config: - connection_string: "postgresql://..." - - type: "webhook" + - type: contract_events_postgres config: - url: "https://api.example.com/events" + host: postgres.example.com + port: 5432 + connect_timeout: 30 + database: defaultdb + username: postgres + password: ${POSTGRES_PASSWORD} + sslmode: require + schema: public + table_prefix: stellar_ ``` -## Data Flow Patterns +Do not commit real database passwords. Use placeholders, CI secrets, or a secret manager. -### Linear Processing -Simple source → processor → consumer flow: -``` -Network → Payments Processor → PostgreSQL -``` - -### Filtered Processing -Pre-filter before main processing: -``` -Network → Contract Filter → Event Processor → Webhook -``` +## Lifecycle -### Fan-Out Processing -One source, multiple destinations: -``` -Network → Transaction Processor → PostgreSQL - └→ S3 Archive -``` - -### Complex DAG -Directed Acyclic Graph for advanced scenarios: -``` -Network → Raw Transactions → Filter A → PostgreSQL - └→ Filter B → Kafka +```mermaid +graph LR + A[Pending] --> B[Deploying] + B --> C[Running] + C --> D[Stopped] + C --> E[Failed] + C --> F[Completed] + D --> B + E --> B ``` -## Performance Characteristics - -### Throughput +| State | Meaning | +|-------|---------| +| `pending` | Config exists and is ready for deployment | +| `deploying` | Flow is allocating and starting runtime resources | +| `running` | Pipeline is processing ledgers | +| `stopped` | Pipeline is paused by a user or API call | +| `failed` | Pipeline stopped because of an error | +| `completed` | Bounded pipeline reached `endLedger` | -Factors affecting pipeline throughput: +## Bounded and continuous pipelines -1. **Processor Complexity**: Simple filters > Complex transformations -2. **Network Selection**: Testnet typically has lower volume -3. **Consumer Batch Size**: Larger batches = higher throughput -4. **Data Volume**: Account-specific > Network-wide +A bounded pipeline has `endLedger` and completes after that ledger. -### Latency - -Expected latencies by configuration: - -- **Real-time** (batch_size: 1): 100-500ms -- **Near real-time** (batch_size: 10): 1-5 seconds -- **Batch** (batch_size: 100): 10-30 seconds - -### Resource Usage - -Pipeline resource consumption varies by: - -- **Data Volume**: More data = more resources -- **Processor Type**: Complex processors use more CPU -- **Consumer Type**: Database consumers may use more memory -- **Historical Processing**: Genesis start uses more resources +```yaml +startLedger: "2434280" +endLedger: "2434281" +``` -## Monitoring & Observability +A continuous pipeline omits `endLedger`. -### Metrics +```yaml +startLedger: "2434280" +``` -Flow provides built-in metrics: +## Create, update, and apply -- **Events Processed**: Total count and rate -- **Processing Latency**: Time from ledger close to delivery -- **Error Rate**: Failed events percentage -- **Consumer Lag**: Backlog size +Flow supports three API patterns. -### Logs +| Pattern | Endpoint | Use it when | +|---------|----------|-------------| +| Create | `POST /api/v1/flow/pipelines/` | You want a new pipeline every time | +| Update | `PUT /api/v1/flow/pipelines/{uuid}/` | You know the pipeline UUID | +| Apply | `POST /api/v1/flow/pipelines/apply/` | You want GitOps-style create/update by name | -Real-time log streaming includes: +`apply` is the best default for CI/CD. It creates the pipeline if it does not exist and updates it if it does. -- **System Logs**: Deployment and lifecycle events -- **Processing Logs**: Data flow information -- **Error Logs**: Detailed error messages -- **Debug Logs**: Verbose troubleshooting info +Running pipelines cannot be updated. Stop the pipeline first. -### Health Checks +## Validation -Automatic health monitoring: +Validate a pipeline without creating it: -```json -{ - "pipeline_id": "pipe_123", - "status": "running", - "health": { - "processor": "healthy", - "consumer": "healthy", - "lag": 2, - "error_rate": 0.01 - } -} +```bash +curl -X POST \ + -H "Authorization: Api-Key $API_KEY" \ + -H "Content-Type: text/yaml" \ + --data-binary @pipeline.yaml \ + "$CONSOLE/api/v1/flow/pipelines/validate/" ``` -## Error Handling - -### Automatic Recovery - -Flow handles common errors automatically: +## Registry -1. **Transient Network Errors**: Automatic retry with backoff -2. **Consumer Unavailable**: Buffer and retry -3. **Rate Limits**: Automatic throttling -4. **Service Restarts**: Resume from last checkpoint +Use the registry endpoints to discover available processors and consumers. -### Manual Intervention +```bash +curl -H "Authorization: Api-Key $API_KEY" \ + "$CONSOLE/api/v1/flow/registry/processors/" -Some errors require user action: +curl -H "Authorization: Api-Key $API_KEY" \ + "$CONSOLE/api/v1/flow/registry/consumers/" +``` -- **Configuration Errors**: Fix and redeploy -- **Authentication Failures**: Update credentials -- **Schema Mismatches**: Adjust processor/consumer -- **Resource Limits**: Optimize configuration +## Billing behavior -## Best Practices +Flow is metered by runtime. Billing starts when a pipeline is running and stops when it is stopped, completed, or failed. -### 1. Start Simple -Begin with basic configurations and add complexity as needed. +Creating a pipeline through the API requires an active subscription unless your team has unbilled pipeline access. If the team is missing a subscription, create/apply returns `402 Payment Required`. -### 2. Use Appropriate Filters -Filter early to reduce processing overhead: -```yaml -processor: - config: - addresses: ["GSPECIFIC..."] # Process only specific accounts -``` +## Next steps -### 3. Optimize Batch Sizes -Balance latency and throughput: -- Real-time alerts: batch_size: 1-10 -- Analytics: batch_size: 50-100 - -### 4. Monitor Usage -Track costs and performance: -- Set up usage alerts -- Review processing metrics -- Optimize based on patterns - -### 5. Plan for Growth -Design pipelines that can scale: -- Use filtering for large datasets -- Consider multiple smaller pipelines -- Plan consumer capacity - -## Security Considerations - -### Credential Management -- All credentials stored in Vault -- Automatic encryption at rest -- No credentials in logs or UI - -### Network Security -- TLS for all connections -- Private networking available -- IP allowlisting supported - -### Access Control -- Team-based permissions -- Audit logs for all actions -- Role-based access control - -## Next Steps - -- Learn about [Processors](../processors/) for data transformation -- Explore [Consumers](../consumers/) for data delivery -- Check our [Getting Started Guide](../getting-started/quickstart.md) -- Review [Pricing](../pricing.md) for cost information \ No newline at end of file +- [Create a pipeline with the API](/docs/flow/getting-started/quickstart) +- [Pipeline API reference](/docs/flow/api) +- [Processors](/docs/flow/processors/) +- [Consumers](/docs/flow/consumers/) diff --git a/docs/flow/consumers/index.md b/docs/flow/consumers/index.md index f0cc06f..24c9b78 100644 --- a/docs/flow/consumers/index.md +++ b/docs/flow/consumers/index.md @@ -1,857 +1,295 @@ --- sidebar_position: 1 -title: Consumers Overview +title: Consumers Reference --- -# Consumers +# Flow consumers -Consumers (Sinks) are the destination components in Flow pipelines that receive processed blockchain data and deliver it to your applications, databases, or storage systems. Each consumer is optimized for specific use cases and integration patterns. +This page lists the consumer IDs currently exposed by the Flow registry. Use these IDs in `spec.consumers[].type` when creating pipelines through the API. -## Available Consumers +```bash +export CONSOLE="https://console.withobsrvr.com" +export API_KEY="your-team-api-key" -### Database Storage - PostgreSQL - -#### [PostgreSQL (Generic)](./postgresql.md) -General-purpose PostgreSQL consumer for ledger data with flexible JSON format. - -**Configuration:** -```yaml -type: postgres -config: - connection_string: "postgresql://user:pass@host:5432/database" - batch_size: 50 -``` - -**Use Cases:** Analytics databases, application backends, historical data storage - ---- - -#### PostgreSQL Bronze -Bronze layer data ingestion to PostgreSQL for data lakehouse architecture. - -**Configuration:** -```yaml -type: postgres_bronze -config: - host: "localhost" - database: "bronze" - username: "user" - password: "password" -``` - -**Use Cases:** Raw data layer storage, medallion architecture - ---- - -#### Buffered PostgreSQL -Buffered batch inserts with retry logic for high-throughput scenarios. - -**Configuration:** -```yaml -type: buffered_postgres -config: - buffer_size: 1000 - flush_interval_ms: 5000 - max_retries: 3 -``` - -**Use Cases:** High-volume data ingestion with batching - ---- - -#### Account Data PostgreSQL -Stores Stellar account data with full metadata. - -**Configuration:** -```yaml -type: account_data_postgres -config: - host: "localhost" - port: 5432 - database: "accounts" - max_open_conns: 20 -``` - -**Use Cases:** Account state tracking and historical analysis - ---- - -#### Asset PostgreSQL -Stores asset/ticker information with batching. - -**Configuration:** -```yaml -type: asset_postgres -config: - batch_size: 100 - connection_string: "postgresql://user:pass@host:5432/assets" -``` - -**Use Cases:** Asset catalog and enrichment data storage - ---- - -#### Asset Enrichment PostgreSQL -Updates asset enrichment data (TOML, auth flags). - -**Configuration:** -```yaml -type: asset_enrichment -config: - connection_string: "postgresql://user:pass@host:5432/assets" -``` - -**Use Cases:** Asset metadata enrichment from stellar.toml - ---- - -#### Payments PostgreSQL -Batched payment transaction storage. - -**Configuration:** -```yaml -type: payments_postgres -config: - batch_size: 1000 - connection_string: "postgresql://user:pass@host:5432/payments" -``` - -**Use Cases:** Payment history and analytics - ---- - -#### Event Payment PostgreSQL -Event-based payment storage with account relationships. - -**Configuration:** -```yaml -type: event_payment_postgres -config: - host: "localhost" - database: "payments" - max_open_conns: 20 -``` - -**Use Cases:** Event-driven payment tracking with foreign keys - ---- - -#### Contract Events PostgreSQL -Soroban contract events storage. - -**Configuration:** -```yaml -type: contract_events_postgres -config: - host: "localhost" - database: "soroban_events" -``` - -**Use Cases:** Smart contract event logging and analysis - ---- - -#### Contract Invocations PostgreSQL -Contract invocations with dual XDR/decoded format, diagnostic events, state changes. - -**Configuration:** -```yaml -type: contract_invocations_postgres -config: - host: "localhost" - database: "contract_invocations" -``` - -**Use Cases:** Complete contract execution tracking - ---- - -#### Extracted Contract Invocations PostgreSQL -Business-logic extracted contract data (funder, recipient, amount, project_id). - -**Configuration:** -```yaml -type: extracted_contract_invocations_postgres -config: - host: "localhost" - database: "business_data" -``` - -**Use Cases:** Business intelligence from smart contracts - ---- - -#### Claimable Balance PostgreSQL -Claimable balance tracking with claimants. - -**Configuration:** -```yaml -type: claimable_balance_postgres -config: - database_url: "postgresql://user:pass@host:5432/claimable_balances" -``` - -**Use Cases:** Claimable balance lifecycle management - ---- - -#### Soroswap PostgreSQL -Soroswap DEX events (pairs, swaps, liquidity). - -**Configuration:** -```yaml -type: soroswap_postgres -config: - host: "localhost" - database: "soroswap" -``` - -**Use Cases:** Soroswap DEX analytics - ---- - -#### Wallet Backend PostgreSQL -Wallet backend with state changes and participants. - -**Configuration:** -```yaml -type: wallet_backend_postgres -config: - host: "localhost" - database: "wallet" -``` - -**Use Cases:** Wallet application backend database - ---- - -### Database Storage - DuckDB - -#### DuckDB (Generic) -General DuckDB storage for analytics-friendly columnar format. - -**Configuration:** -```yaml -type: duckdb -config: - db_path: "/path/to/data.duckdb" -``` - -**Use Cases:** Local analytics, data science workflows - ---- - -#### DuckLake -DuckDB lakehouse pattern with schema registry. - -**Configuration:** -```yaml -type: ducklake -config: - db_path: "ducklake.duckdb" - catalog_name: "main" - schema_name: "bronze" - table_prefix: "stellar_" -``` - -**Use Cases:** Data lakehouse architecture with managed schemas - ---- - -#### DuckLake Enhanced -Enhanced DuckLake with better performance and features. - -**Configuration:** -```yaml -type: ducklake_enhanced -config: - db_path: "ducklake_enhanced.duckdb" - batch_size: 100 - flush_interval: 5 -``` - -**Use Cases:** High-performance data lakehouse - ---- - -#### Bronze DuckDB -Bronze layer DuckDB ingestion with appenders for medallion architecture. - -**Configuration:** -```yaml -type: bronze_duckdb -config: - db_path: "bronze.duckdb" - batch_size: 100 - flush_interval_seconds: 10 -``` - -**Use Cases:** Raw data ingestion for data lakehouse - ---- - -#### Account Data DuckDB -Account data with schema validation. - -**Configuration:** -```yaml -type: account_data_duckdb -config: - db_path: "accounts.duckdb" -``` - -**Use Cases:** Account analytics in DuckDB - ---- - -#### Assets DuckDB -Asset catalog with upsert logic. - -**Configuration:** -```yaml -type: assets_duckdb -config: - db_path: "assets.duckdb" +curl -H "Authorization: Api-Key $API_KEY" \ + "$CONSOLE/api/v1/flow/registry/consumers/" ``` -**Use Cases:** Asset tracking and analytics +:::warning Keep credentials out of source control +Consumer configs often include database passwords, connection strings, or cloud credentials. Use placeholders, CI secrets, or the Flow secrets API instead of committing live values. +::: ---- +## Consumer summary -#### Contract Events DuckDB -Soroban contract events with analytics views. +| ID | Name | Description | +|----|------|-------------| +| `account_data_postgres` | Account Data PostgreSQL | Store Stellar account data in a specialized PostgreSQL schema | +| `contract_invocations_postgres` | Contract Invocations PostgreSQL | Store Soroban contract invocations in a specialized PostgreSQL schema | +| `contract_events_postgres` | Contract Events PostgreSQL | Store Soroban contract events in a specialized PostgreSQL schema | +| `soroswap_postgres` | SwapService PostgreSQL | Store SwapService events in a specialized PostgreSQL schema | +| `save_latest_ledger_redis` | Redis Ledger Storage | Store latest ledger information in Redis | +| `contract_data_postgres` | Contract Data PostgreSQL | Save contract data to PostgreSQL with configurable schema | +| `extracted_contract_invocations_postgres` | Extracted Contract Invocations PostgreSQL | Save extracted contract invocation business data to PostgreSQL with optimized schema | +| `event_payment_postgres` | Event Payment PostgreSQL | Saves event payment data to PostgreSQL with accounts tracking | +| `google_pubsub_v2` | Google Pub/Sub (V2) | Publishes event payments to Google Cloud Pub/Sub in V2 message format | -**Configuration:** -```yaml -type: contract_events_duckdb -config: - db_path: "soroban_events.duckdb" -``` +## Consumer configuration reference -**Use Cases:** Contract event analytics with built-in views +### `account_data_postgres` — Account Data PostgreSQL ---- +Store Stellar account data in a specialized PostgreSQL schema -#### Soroswap Pairs DuckDB -Soroswap pair and sync events. +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `host` | string | Yes | | PostgreSQL server hostname | +| `port` | integer | Yes | `5432` | PostgreSQL server port | +| `database` | string | Yes | | PostgreSQL database name | +| `username` | string | Yes | | PostgreSQL username | +| `password` | string sensitive | Yes | | PostgreSQL password | +| `sslmode` | string enum: `disable`, `require`, `verify-ca`, `verify-full` | No | `disable` | PostgreSQL SSL mode | +| `max_open_conns` | integer (min 1) | No | `10` | Maximum number of open connections to the database | +| `max_idle_conns` | integer (min 1) | No | `5` | Maximum number of idle connections in the pool | -**Configuration:** -```yaml -type: soroswap_pairs_duckdb -config: - db_path: "soroswap_pairs.duckdb" -``` - -**Use Cases:** DEX pair tracking and reserves - ---- -#### Soroswap Router DuckDB -Soroswap router transactions. +**Example** -**Configuration:** ```yaml -type: soroswap_router_duckdb -config: - db_path: "soroswap_router.duckdb" +consumers: + - type: account_data_postgres + config: + host: postgres.example.com + port: 5432 + database: defaultdb + username: postgres + password: "${POSTGRES_PASSWORD}" + sslmode: require + max_open_conns: 10 + max_idle_conns: 5 ``` -**Use Cases:** Router analytics and swap tracking +### `contract_invocations_postgres` — Contract Invocations PostgreSQL ---- +Store Soroban contract invocations in a specialized PostgreSQL schema -### Database Storage - Other +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `host` | string | Yes | | PostgreSQL server hostname | +| `port` | integer | Yes | `5432` | PostgreSQL server port | +| `database` | string | Yes | | PostgreSQL database name | +| `username` | string | Yes | | PostgreSQL username | +| `password` | string | Yes | | PostgreSQL password | +| `sslmode` | string enum: `disable`, `require`, `verify-ca`, `verify-full` | Yes | `disable` | PostgreSQL SSL mode | +| `max_open_conns` | integer | Yes | `10` | Maximum number of open connections | +| `max_idle_conns` | integer | Yes | `5` | Maximum number of idle connections | -#### SQLite (Soroswap) -Lightweight SQLite versions of Soroswap consumers. -**Configuration:** -```yaml -type: soroswap_pairs_sqlite -config: - db_path: "soroswap.db" -``` +**Example** -**Use Cases:** Portable DEX data storage, development - ---- - -#### ClickHouse -OLAP database with materialized views for real-time analytics. - -**Configuration:** ```yaml -type: clickhouse -config: - address: "localhost:9000" - database: "stellar" - username: "default" - password: "" - max_open_conns: 25 +consumers: + - type: contract_invocations_postgres + config: + host: postgres.example.com + port: 5432 + database: defaultdb + username: postgres + password: "${POSTGRES_PASSWORD}" + sslmode: require + max_open_conns: 10 + max_idle_conns: 5 ``` -**Use Cases:** Payment stats, price analytics, trustlines, high-volume analytics +### `contract_events_postgres` — Contract Events PostgreSQL ---- +Store Soroban contract events in a specialized PostgreSQL schema -#### MongoDB -Document-based storage for flexible schemas. +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `host` | string | Yes | | PostgreSQL server hostname | +| `port` | integer | Yes | | PostgreSQL server port | +| `database` | string | Yes | | PostgreSQL database name | +| `username` | string | Yes | | PostgreSQL username | +| `password` | string | Yes | | PostgreSQL password | +| `sslmode` | string enum: `disable`, `require`, `verify-ca`, `verify-full` | Yes | `disable` | PostgreSQL SSL mode | +| `max_open_conns` | integer | Yes | `10` | Maximum number of open connections | +| `max_idle_conns` | integer | Yes | `5` | Maximum number of idle connections | -**Configuration:** -```yaml -type: mongodb -config: - uri: "mongodb://localhost:27017" - database: "stellar" - collection: "transactions" -``` - -**Use Cases:** Flexible document storage, semi-structured data ---- +**Example** -#### TimescaleDB -Time-series database for temporal analytics. - -**Configuration:** ```yaml -type: timescaledb -config: - host: "localhost" - database: "timeseries" +consumers: + - type: contract_events_postgres + config: + host: postgres.example.com + port: 5432 + database: defaultdb + username: postgres + password: "${POSTGRES_PASSWORD}" + sslmode: require + max_open_conns: 10 + max_idle_conns: 5 ``` -**Use Cases:** Time-series data and analytics +### `soroswap_postgres` — SwapService PostgreSQL ---- +Store SwapService events in a specialized PostgreSQL schema -### Caching - Redis +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `host` | string | Yes | | PostgreSQL server hostname | +| `port` | integer | Yes | `5432` | PostgreSQL server port | +| `database` | string | Yes | | PostgreSQL database name | +| `username` | string | Yes | | PostgreSQL username | +| `password` | string | Yes | | PostgreSQL password | +| `sslmode` | string enum: `disable`, `require`, `verify-ca`, `verify-full` | Yes | `disable` | PostgreSQL SSL mode | +| `max_open_conns` | integer | Yes | `10` | Maximum number of open connections | +| `max_idle_conns` | integer | Yes | `5` | Maximum number of idle connections | -#### Redis (Generic) -Multi-operation Redis storage (payments, accounts, assets, prices, offers, trustlines). -**Configuration:** -```yaml -type: redis -config: - redis_url: "redis://localhost:6379" - key_prefix: "flow:" - ttl_hours: 24 - use_tls: true -``` +**Example** -**Use Cases:** Fast lookups, caching, real-time data - ---- - -#### Payments Redis -Payment-specific Redis storage with indices. - -**Configuration:** ```yaml -type: payments_redis -config: - redis_url: "redis://localhost:6379" - key_prefix: "payment:" - ttl_hours: 48 +consumers: + - type: soroswap_postgres + config: + host: postgres.example.com + port: 5432 + database: defaultdb + username: postgres + password: "${POSTGRES_PASSWORD}" + sslmode: require + max_open_conns: 10 + max_idle_conns: 5 ``` -**Use Cases:** Payment caching and fast retrieval - ---- - -#### Latest Ledger Redis -Latest ledger tracking with history. +### `save_latest_ledger_redis` — Redis Ledger Storage -**Configuration:** -```yaml -type: latest_ledger_redis -config: - redis_url: "redis://localhost:6379" - key_prefix: "ledger:" - use_tls: true -``` +Store latest ledger information in Redis -**Use Cases:** Ledger progress tracking +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `redis_address` | string | Yes | `:6379` | Redis server address (host:port) | +| `redis_password` | string sensitive | No | | Redis server password | +| `redis_db` | integer (min 0, max 15) | No | `0` | Redis database number | +| `key_prefix` | string | No | `stellar:ledger:` | Prefix for Redis keys | +| `use_tls` | boolean | No | `true` | Enable TLS/SSL connection to Redis | ---- -#### Orderbook Redis -Order book data storage. +**Example** -**Configuration:** ```yaml -type: orderbook_redis -config: - redis_url: "redis://localhost:6379" +consumers: + - type: save_latest_ledger_redis + config: + redis_address: "redis.example.com:6379" + redis_db: 0 + key_prefix: "stellar:ledger:" + use_tls: true ``` -**Use Cases:** Trading orderbook caching +### `contract_data_postgres` — Contract Data PostgreSQL ---- +Save contract data to PostgreSQL with configurable schema -#### Market Analytics Redis -Market analytics and metrics. +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `database_url` | string (uri) sensitive | Yes | | PostgreSQL connection string | +| `table_name` | string | Yes | `contract_data` | PostgreSQL table name | +| `create_table_if_not_exists` | boolean | No | `true` | Automatically create table if missing | +| `batch_size` | integer (min 1, max 1000) | No | `100` | Number of records to batch before writing | +| `max_open_conns` | integer (min 1, max 100) | No | `25` | Maximum database connections | +| `max_idle_conns` | integer (min 1, max 50) | No | `5` | Maximum idle database connections | -**Configuration:** -```yaml -type: market_analytics_redis -config: - redis_url: "redis://localhost:6379" -``` -**Use Cases:** Trading analytics and market data - ---- +**Example** -### Cloud Storage - -#### Google Cloud Storage -Upload to Google Cloud Storage buckets. - -**Configuration:** ```yaml -type: gcs -config: - bucket_name: "my-stellar-data" - object_prefix: "ledgers/" - credentials_file: "/path/to/credentials.json" +consumers: + - type: contract_data_postgres + config: + database_url: "postgres://user:${POSTGRES_PASSWORD}@postgres.example.com:5432/defaultdb?sslmode=require" + table_name: "contract_data" + create_table_if_not_exists: true + batch_size: 100 + max_open_conns: 25 + max_idle_conns: 5 ``` -**Use Cases:** Cloud backup and archival +### `extracted_contract_invocations_postgres` — Extracted Contract Invocations PostgreSQL ---- +Save extracted contract invocation business data to PostgreSQL with optimized schema -#### Parquet Files -Parquet file generation with cloud storage support (local/S3/GCS). +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `host` | string | Yes | | PostgreSQL server hostname | +| `port` | integer | Yes | `5432` | PostgreSQL server port | +| `database` | string | Yes | | PostgreSQL database name | +| `username` | string | Yes | | PostgreSQL username | +| `password` | string sensitive | Yes | | PostgreSQL password | +| `sslmode` | string enum: `disable`, `require`, `verify-ca`, `verify-full` | No | `disable` | PostgreSQL SSL mode | +| `max_open_conns` | integer (min 1) | No | `10` | Maximum number of open connections to the database | +| `max_idle_conns` | integer (min 1) | No | `5` | Maximum number of idle connections in the pool | +| `connect_timeout` | integer (min 1) | No | `30` | Connection timeout in seconds | -**Configuration:** -```yaml -type: parquet -config: - storage_type: "s3" - s3_bucket: "my-stellar-data" - output_path: "parquet/ledgers/" -``` - -**Use Cases:** Columnar data export for analytics - ---- -#### Ledger Parquet -Ledger-specific Parquet files. +**Example** -**Configuration:** ```yaml -type: ledger_parquet -config: - storage_type: "gcs" - gcs_bucket: "my-stellar-ledgers" - output_path: "parquet/" +consumers: + - type: extracted_contract_invocations_postgres + config: + host: postgres.example.com + port: 5432 + database: defaultdb + username: postgres + password: "${POSTGRES_PASSWORD}" + sslmode: require + max_open_conns: 10 + max_idle_conns: 5 + connect_timeout: 30 ``` -**Use Cases:** Ledger data archival in Parquet format - ---- - -### Streaming & Messaging - -#### Google Pub/Sub -Publisher for Google Pub/Sub with EventPayment support. +### `event_payment_postgres` — Event Payment PostgreSQL -**Configuration:** -```yaml -type: pubsub -config: - project_id: "my-project" - topic_id: "stellar-events" - credentials_file: "/path/to/credentials.json" -``` +Saves event payment data to PostgreSQL with accounts tracking -**Use Cases:** Event streaming to Google Pub/Sub +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `connectionString` | string (uri) sensitive | Yes | | PostgreSQL connection string (e.g., postgres://user:pass@host:5432/db) | +| `batchSize` | integer (min 1, max 1000) | No | `1` | Number of records to batch before committing | ---- -#### Google Pub/Sub V2 -V2 format with chain identifier (StellarMainnet/Testnet). +**Example** -**Configuration:** ```yaml -type: pubsub_v2 -config: - project_id: "my-project" - topic_id: "stellar-events-v2" - chain_identifier: "StellarMainnet" +consumers: + - type: event_payment_postgres + config: + connectionString: "postgres://user:${POSTGRES_PASSWORD}@postgres.example.com:5432/defaultdb?sslmode=require" + batchSize: 1 ``` -**Use Cases:** Multi-chain Pub/Sub publishing +### `google_pubsub_v2` — Google Pub/Sub (V2) ---- +Publishes event payments to Google Cloud Pub/Sub in V2 message format -#### ZeroMQ -Low-latency message publishing. +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `project_id` | string | Yes | | Google Cloud project ID | +| `topic_id` | string | Yes | | Pub/Sub topic name | +| `chain_identifier` | string enum: `StellarMainnet`, `StellarTestnet` | Yes | `StellarTestnet` | Stellar network identifier | +| `credentials_json` | string (textarea) sensitive | Yes | | Google Cloud service account JSON key (paste the entire JSON content) | -**Configuration:** -```yaml -type: zeromq -config: - endpoint: "tcp://localhost:5555" -``` - -**Use Cases:** High-performance IPC, minimal latency - ---- -### Real-time Communications +**Example** -#### WebSocket -WebSocket server with client filtering and queuing. - -**Configuration:** ```yaml -type: websocket -config: - port: 8080 - path: "/ws" - max_queue_size: 1000 +consumers: + - type: google_pubsub_v2 + config: + project_id: "my-gcp-project" + topic_id: "stellar-events" + chain_identifier: "StellarTestnet" + credentials_json: "${GOOGLE_APPLICATION_CREDENTIALS_JSON}" ``` - -**Use Cases:** Real-time browser/app updates with custom filters - ---- - -### File Export - -#### Excel -Excel spreadsheet export. - -**Configuration:** -```yaml -type: excel -config: - file_path: "/path/to/output.xlsx" - sheet_name: "Data" -``` - -**Use Cases:** Manual analysis and reporting - ---- - -#### Latest Ledger Excel -Ledger-specific Excel export. - -**Configuration:** -```yaml -type: latest_ledger_excel -config: - file_path: "/path/to/ledgers.xlsx" -``` - -**Use Cases:** Ledger progress tracking in Excel - ---- - -### AI/ML Integration - -#### Anthropic Claude -Batches messages and sends to Claude API for analysis. - -**Configuration:** -```yaml -type: claude -config: - anthropic_api_key: "sk-ant-..." - batch_size: 10 - flush_interval_seconds: 30 -``` - -**Use Cases:** AI-powered transaction analysis and insights - ---- - -### Notifications - -#### Notification Dispatcher -Multi-channel notifications (Slack, email, webhook) with rule-based filtering. - -**Configuration:** -```yaml -type: notification_dispatcher -config: - slack_token: "xoxb-..." - webhook_urls: - - "https://hooks.slack.com/services/..." - rules: - - condition: "amount > 1000000" - channel: "high-value-payments" -``` - -**Use Cases:** Alert system for threshold-based monitoring - ---- - -### Data Transformation - -#### Silver Ingester -Bronze to Silver layer transformation for medallion architecture. - -**Configuration:** -```yaml -type: silver_ingester -config: - source_db: "bronze.duckdb" - target_db: "silver.duckdb" -``` - -**Use Cases:** Curated data layer processing - ---- - -### Debugging & Development - -#### Stdout -Prints JSON to stdout for debugging. - -**Configuration:** -```yaml -type: stdout -``` - -**Use Cases:** Development, debugging, testing - ---- - -#### Debug Logger -Detailed message inspection with field limits. - -**Configuration:** -```yaml -type: debug_logger -config: - name: "pipeline-debug" - log_prefix: "DEBUG" - max_fields: 10 -``` - -**Use Cases:** Deep debugging of message structure - ---- - -#### Log Debug -Logs message types and metadata. - -**Configuration:** -```yaml -type: log_debug -config: - log_level: "info" -``` - -**Use Cases:** Pipeline flow debugging - ---- - -## Choosing the Right Consumer - -Consider these factors when selecting a consumer: - -### 1. **Data Access Patterns** -- **Real-time queries**: PostgreSQL, Redis -- **Batch analytics**: S3, DuckDB -- **Stream processing**: Kafka, Webhook -- **Embedded/Edge**: SQLite, DuckDB - -### 2. **Scale Requirements** -- **High volume**: Kafka, S3 -- **Moderate volume**: PostgreSQL, Webhook -- **Low volume**: SQLite, ZeroMQ - -### 3. **Infrastructure** -- **Managed services**: PostgreSQL, S3 -- **Self-hosted**: All options -- **No infrastructure**: SQLite, DuckDB - -### 4. **Integration Needs** -- **SQL queries**: PostgreSQL, DuckDB, SQLite -- **REST APIs**: Webhook -- **Stream processing**: Kafka, ZeroMQ -- **Key-value access**: Redis - -## Consumer Configuration - -All consumers support configuration through the Flow pipeline builder: - -```json -{ - "consumer_type": "postgres", - "config": { - "connection_string": "postgresql://user:pass@host/db", - "batch_size": 100 - } -} -``` - -## Security Considerations - -### Credential Management -- Credentials are stored securely in Vault -- Never exposed in logs or UI -- Automatic rotation support -- Encrypted in transit - -### Connection Security -- TLS/SSL support for all network consumers -- Authentication mechanisms: - - PostgreSQL: Username/password, SSL certs - - Kafka: SASL, SSL - - S3: IAM credentials - - Webhook: Shared secrets - -## Performance Optimization - -### Batch Processing -Most consumers support batch processing for efficiency: -- Configure appropriate batch sizes -- Balance between latency and throughput -- Consider memory constraints - -### Connection Pooling -Database consumers maintain connection pools: -- Reuse connections for better performance -- Configure pool sizes based on load -- Monitor connection health - -### Parallel Processing -Some consumers support parallel writes: -- Kafka partitions -- S3 multipart uploads -- PostgreSQL parallel inserts - -## Monitoring & Troubleshooting - -### Health Checks -- Connection status monitoring -- Write failure tracking -- Latency measurements -- Queue depth (for streaming consumers) - -### Common Issues -- **Connection failures**: Check credentials and network -- **Performance degradation**: Adjust batch sizes -- **Data loss**: Enable consumer acknowledgments -- **Schema mismatches**: Verify data formats - -## Best Practices - -1. **Start Simple**: Begin with PostgreSQL or Webhook -2. **Plan for Scale**: Choose consumers that can grow with your needs -3. **Monitor Performance**: Track metrics and adjust configurations -4. **Handle Failures**: Implement retry logic and dead letter queues -5. **Secure Credentials**: Use Vault integration for sensitive data - -## Next Steps - -- Explore individual consumer documentation for detailed configuration -- Check our [Getting Started Guide](../getting-started/quickstart.md) for implementation examples -- Learn about [processors](../processors) to transform your data -- Review [Flow Overview](../overview.md) for architecture details diff --git a/docs/flow/consumers/postgresql.md b/docs/flow/consumers/postgresql.md index 47a77a0..d5246a0 100644 --- a/docs/flow/consumers/postgresql.md +++ b/docs/flow/consumers/postgresql.md @@ -1,373 +1,63 @@ --- sidebar_position: 2 -title: PostgreSQL +title: PostgreSQL Consumers --- -# PostgreSQL Consumer +# PostgreSQL consumers -The PostgreSQL consumer delivers processed blockchain data to PostgreSQL databases, offering flexible schema options and enterprise-grade reliability for data storage and analytics. +Flow currently exposes specialized PostgreSQL consumers, not a generic `postgres` consumer. Pick the consumer that matches the processor output type. -## Overview +## Available PostgreSQL consumers -PostgreSQL is the most versatile consumer in Flow, supporting: -- **Generic schemas** for flexible data storage -- **Specialized schemas** optimized for specific data types -- **Custom schemas** for your specific requirements -- **Batch processing** for optimal performance -- **Connection pooling** for scalability +| Consumer ID | Use with processor IDs | Description | +|-------------|------------------------|-------------| +| `account_data_postgres` | `account_data` | Store Stellar account data in a specialized PostgreSQL schema | +| `contract_invocations_postgres` | `contract_invocation`, `contract_filter` | Store Soroban contract invocations in a specialized PostgreSQL schema | +| `contract_events_postgres` | `contract_event`, `contract_filter` | Store Soroban contract events in a specialized PostgreSQL schema | +| `soroswap_postgres` | `soroswap` | Store SwapService events in a specialized PostgreSQL schema | +| `contract_data_postgres` | `contract_data` | Save contract data to PostgreSQL with configurable schema | +| `extracted_contract_invocations_postgres` | `contract_invocation`, `contract_filter`, `extracted_contract_invocation` | Save extracted contract invocation business data to PostgreSQL with optimized schema | +| `event_payment_postgres` | `event_payment_extractor` | Saves event payment data to PostgreSQL with accounts tracking | -## Configuration +## Connection-field based consumers -### Basic PostgreSQL Consumer +Most specialized PostgreSQL consumers use separate connection fields: -The standard PostgreSQL consumer stores data in a flexible JSON format. - -#### Parameters - -| Parameter | Type | Required | Default | Description | -|-----------|------|----------|---------|-------------| -| `connection_string` | string | Yes | - | PostgreSQL connection string | -| `batch_size` | integer | No | 5 | Records per batch (1-100) | - -#### Example Configuration - -```json -{ - "type": "postgres", - "config": { - "connection_string": "postgresql://user:password@host:5432/database?sslmode=require", - "batch_size": 50 - } -} +```yaml +consumers: + - type: contract_events_postgres + config: + host: postgres.example.com + port: 5432 + database: defaultdb + username: postgres + password: ${POSTGRES_PASSWORD} + sslmode: require + max_open_conns: 10 + max_idle_conns: 5 ``` -### Connection String Format +These consumers use that shape: +- `account_data_postgres` +- `contract_invocations_postgres` +- `contract_events_postgres` +- `soroswap_postgres` +- `extracted_contract_invocations_postgres` -``` -postgresql://[user[:password]@][host][:port][/dbname][?param1=value1&...] -``` +## Connection-string consumers -Common parameters: -- `sslmode`: `disable`, `require`, `verify-ca`, `verify-full` -- `connect_timeout`: Connection timeout in seconds -- `application_name`: Identify your application in pg_stat_activity +Some consumers use a single connection string instead: -## Specialized PostgreSQL Consumers - -Flow provides optimized consumers for specific data types: - -### Account Data PostgreSQL - -Stores Stellar account information with optimized schema. - -```json -{ - "type": "account_data_postgres", - "config": { - "host": "localhost", - "port": 5432, - "database": "stellar_accounts", - "username": "user", - "password": "password", - "sslmode": "require", - "max_open_conns": 20, - "max_idle_conns": 10 - } -} +```yaml +consumers: + - type: contract_data_postgres + config: + database_url: postgres://user:${POSTGRES_PASSWORD}@postgres.example.com:5432/defaultdb?sslmode=require + table_name: contract_data ``` -### Contract Events PostgreSQL - -Optimized for Soroban contract events with indexed topics. - -```json -{ - "type": "contract_events_postgres", - "config": { - "host": "localhost", - "port": 5432, - "database": "contract_events", - "username": "user", - "password": "password" - } -} -``` - -### Contract Data PostgreSQL - -Supports custom schemas for contract data storage. - -```json -{ - "type": "contract_data_postgres", - "config": { - "database_url": "postgresql://user:password@host/db", - "table_name": "contract_storage", - "create_table_if_not_exists": true, - "batch_size": 100, - "schema_config": { - "columns": [ - {"name": "contract_id", "type": "text"}, - {"name": "key", "type": "text"}, - {"name": "value", "type": "jsonb"}, - {"name": "ledger", "type": "bigint"} - ] - } - } -} -``` - -## Schema Design - -### Generic Schema (Default) - -The basic PostgreSQL consumer uses a flexible schema: - -```sql -CREATE TABLE flow_data ( - id SERIAL PRIMARY KEY, - ledger BIGINT NOT NULL, - timestamp TIMESTAMP WITH TIME ZONE, - data JSONB NOT NULL, - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() -); - -CREATE INDEX idx_flow_data_ledger ON flow_data(ledger); -CREATE INDEX idx_flow_data_timestamp ON flow_data(timestamp); -CREATE INDEX idx_flow_data_jsonb ON flow_data USING GIN(data); -``` - -### Custom Schema Configuration - -For specialized consumers that support schema configuration: - -```json -{ - "schema_config": { - "columns": [ - { - "name": "transaction_hash", - "type": "text", - "nullable": false, - "primary_key": true - }, - { - "name": "amount", - "type": "numeric", - "nullable": false - }, - { - "name": "memo", - "type": "text", - "nullable": true - }, - { - "name": "created_at", - "type": "timestamp", - "default": "NOW()" - } - ], - "indexes": [ - { - "columns": ["created_at"], - "type": "btree" - }, - { - "columns": ["memo"], - "type": "hash", - "where": "memo IS NOT NULL" - } - ] - } -} -``` - -## Performance Optimization - -### Batch Size Tuning - -Choose batch size based on your use case: - -- **Small batches (1-10)**: Lower latency, real-time updates -- **Medium batches (10-50)**: Balanced performance -- **Large batches (50-100)**: Maximum throughput - -### Connection Pool Configuration - -For specialized consumers: - -```json -{ - "max_open_conns": 25, // Maximum open connections - "max_idle_conns": 5, // Maximum idle connections - "conn_max_lifetime": 300 // Connection lifetime in seconds -} -``` - -### Indexing Strategy - -Create appropriate indexes for your queries: - -```sql --- For time-series queries -CREATE INDEX idx_timestamp ON your_table(timestamp DESC); - --- For filtering by specific fields -CREATE INDEX idx_account ON your_table(account_id); - --- For JSONB queries -CREATE INDEX idx_data_gin ON your_table USING GIN(data); - --- For complex queries -CREATE INDEX idx_composite ON your_table(account_id, timestamp DESC); -``` - -## Use Cases - -### Analytics Database - -```json -{ - "processor": { - "type": "raw_transactions" - }, - "consumer": { - "type": "postgres", - "config": { - "connection_string": "postgresql://analytics:pass@analytics.db/stellar", - "batch_size": 100 - } - } -} -``` - -### Real-time Application Backend - -```json -{ - "processor": { - "type": "payments_memo", - "config": { - "memo_text": "app-payment" - } - }, - "consumer": { - "type": "postgres", - "config": { - "connection_string": "postgresql://app:pass@app.db/payments", - "batch_size": 1 - } - } -} -``` - -### Historical Data Warehouse - -```json -{ - "processor": { - "type": "account_balance", - "config": { - "account_ids": ["GXXXX...", "GYYYY..."] - } - }, - "consumer": { - "type": "postgres", - "config": { - "connection_string": "postgresql://warehouse:pass@warehouse.db/balances", - "batch_size": 50 - } - } -} -``` - -## Monitoring & Maintenance - -### Query Performance - -Monitor slow queries: - -```sql -SELECT query, calls, total_time, mean_time -FROM pg_stat_statements -WHERE mean_time > 100 -ORDER BY mean_time DESC; -``` - -### Table Maintenance - -Regular maintenance tasks: - -```sql --- Analyze tables for query optimization -ANALYZE your_table; - --- Vacuum to reclaim space -VACUUM ANALYZE your_table; - --- Reindex for performance -REINDEX TABLE your_table; -``` - -### Connection Monitoring - -Check active connections: - -```sql -SELECT pid, usename, application_name, state, query_start -FROM pg_stat_activity -WHERE datname = 'your_database'; -``` - -## Security Best Practices - -1. **Use SSL/TLS**: Always set `sslmode=require` or stronger -2. **Credential Rotation**: Regularly rotate database passwords -3. **Least Privilege**: Create specific users with minimal permissions -4. **Network Security**: Use VPC/private networks when possible -5. **Audit Logging**: Enable PostgreSQL audit logging - -### Example User Setup - -```sql --- Create a dedicated Flow user -CREATE USER flow_user WITH PASSWORD 'secure_password'; - --- Grant minimal permissions -GRANT CONNECT ON DATABASE stellar_data TO flow_user; -GRANT USAGE ON SCHEMA public TO flow_user; -GRANT CREATE ON SCHEMA public TO flow_user; -GRANT INSERT, SELECT ON ALL TABLES IN SCHEMA public TO flow_user; -``` - -## Troubleshooting - -### Connection Issues - -**Error: Connection refused** -- Verify host and port -- Check PostgreSQL is running -- Verify firewall rules - -**Error: Authentication failed** -- Check username/password -- Verify pg_hba.conf settings -- Ensure user has CONNECT permission - -### Performance Issues - -**Slow inserts** -- Increase batch_size -- Check for lock contention -- Review indexes (too many can slow inserts) - -**High memory usage** -- Reduce batch_size -- Check for memory leaks -- Monitor connection pool size - -## Related Consumers +Connection-string consumers: +- `contract_data_postgres` uses `database_url` +- `event_payment_postgres` uses `connectionString` -- Redis - For real-time caching -- S3 - For data archival -- Kafka - For stream processing \ No newline at end of file +For the complete field list, see [Consumers Reference](./). \ No newline at end of file diff --git a/docs/flow/getting-started/quickstart.md b/docs/flow/getting-started/quickstart.md index 3579c88..2569f0e 100644 --- a/docs/flow/getting-started/quickstart.md +++ b/docs/flow/getting-started/quickstart.md @@ -3,187 +3,200 @@ sidebar_position: 2 title: Quickstart Guide --- -# Quickstart Guide +# Flow quickstart -Get your first Flow pipeline running in minutes. This guide walks you through creating a payment tracking pipeline that monitors Stellar payments and stores them in PostgreSQL. +Create a Flow pipeline from YAML and deploy it with the API. This example reads Stellar testnet ledger files, extracts Soroban contract events, and writes them to PostgreSQL. + +If you only need to query decoded Stellar data, start with [Obsrvr Lake](/docs/lake/overview). Use Flow when you need a custom processor and destination. + +:::warning Do not commit credentials +Pipeline files often contain database credentials. Store them in your deployment secret manager or inject them at deploy time. The examples below use placeholders. +::: ## Prerequisites -Before you begin, ensure you have: +- An Obsrvr account in [Console](https://console.withobsrvr.com) +- A Team API key +- An active Flow subscription, unless your team has unbilled pipeline access +- A PostgreSQL database reachable from the Flow runtime +- `curl` -1. **Obsrvr Account**: [Sign up](https://console.withobsrvr.com) and join the Flow waitlist -2. **Active Subscription**: Flow requires an active subscription ($0.003/minute) -3. **PostgreSQL Database**: For storing the processed data (or use our managed option) +Set your environment: -## Step 1: Access Flow +```bash +export CONSOLE="https://console.withobsrvr.com" +export API_KEY="your-team-api-key" +``` -Once approved from the waitlist: +All Flow API requests use: -1. Log into the [Obsrvr Console](https://console.withobsrvr.com) -2. Navigate to **Flow** in the main menu -3. Click **Create Pipeline** to start the configuration wizard +```bash +Authorization: Api-Key $API_KEY +``` -## Step 2: Configure Your Pipeline +## 1. Create a pipeline file -### Network Selection +Create `contract-events-to-postgres.yaml`: -Choose your target network: -- **Mainnet**: For production data -- **Testnet**: For development and testing +```yaml +apiVersion: flow.obsrvr.com/v1 +kind: Pipeline +metadata: + name: ContractEventsToPostgres +spec: + network: testnet + startLedger: "2434280" + endLedger: "2434281" + processors: + - type: contract_event + config: + network_passphrase: Test SDF Network ; September 2015 + consumers: + - type: contract_events_postgres + config: + host: postgres.example.com + port: 5432 + connect_timeout: 30 + database: defaultdb + username: postgres + password: ${POSTGRES_PASSWORD} + sslmode: require + schema: public + table_prefix: stellar_ +``` -### Start Ledger Configuration +This pipeline processes ledgers `2434280` through `2434281` on testnet and writes contract events to PostgreSQL tables with the `stellar_` prefix. -Select where to begin processing: -- **Latest**: Start from the most recent ledger -- **Genesis**: Process from the beginning (historical data) -- **Specific Ledger**: Enter a ledger number to start from +## 2. Validate the config -### Select a Processor +```bash +curl -X POST \ + -H "Authorization: Api-Key $API_KEY" \ + -H "Content-Type: text/yaml" \ + --data-binary @contract-events-to-postgres.yaml \ + "$CONSOLE/api/v1/flow/pipelines/validate/" +``` -For this example, choose **Payments with Memo**: +A valid config returns: ```json { - "type": "payments_memo", - "config": { - "memo_text": "invoice", - "min_amount": "10", - "asset_code": "USDC" + "valid": true, + "errors": [], + "warnings": [], + "effective_config": { + "apiVersion": "flow.obsrvr.com/v1", + "kind": "Pipeline", + "metadata": {"name": "ContractEventsToPostgres"}, + "spec": {"...": "..."} } } ``` -This configuration will: -- Filter payments containing "invoice" in the memo -- Only process payments >= 10 USDC -- Track USDC payments specifically +## 3. Apply and start the pipeline + +```bash +curl -X POST \ + -H "Authorization: Api-Key $API_KEY" \ + -H "Content-Type: text/yaml" \ + --data-binary @contract-events-to-postgres.yaml \ + "$CONSOLE/api/v1/flow/pipelines/apply/?auto_start=true" +``` -### Configure the Consumer +`apply` is idempotent. If `ContractEventsToPostgres` does not exist, Flow creates it. If it exists and is stopped, Flow updates it. -Select **PostgreSQL** as your destination: +Response: ```json { - "type": "postgres", - "config": { - "connection_string": "postgresql://user:password@host:5432/payments", - "batch_size": 50 - } + "pipeline_id": "47d6e2c8-8fb8-46a5-9222-932f2f9d7ac9", + "action": "created", + "status": "running", + "warnings": [], + "errors": [] } ``` -## Step 3: Deploy Your Pipeline - -1. Review your configuration -2. Name your pipeline (e.g., "invoice-payment-tracker") -3. Click **Deploy Pipeline** +Save the `pipeline_id` as `PIPELINE_UUID` for later commands. -The deployment process: -- Validates your configuration -- Securely stores credentials in Vault -- Deploys to Obsrvr's infrastructure -- Begins processing immediately +```bash +export PIPELINE_UUID="47d6e2c8-8fb8-46a5-9222-932f2f9d7ac9" +``` -## Step 4: Monitor Your Pipeline +## 4. List and inspect pipelines -### Pipeline Status +```bash +curl -H "Authorization: Api-Key $API_KEY" \ + "$CONSOLE/api/v1/flow/pipelines/" +``` -Your pipeline will progress through these states: -- `pending` → `deploying` → `running` +Get one pipeline: -### View Logs +```bash +curl -H "Authorization: Api-Key $API_KEY" \ + "$CONSOLE/api/v1/flow/pipelines/$PIPELINE_UUID/" +``` -Click on your pipeline to access: -- Real-time log streaming -- Processing statistics -- Error messages (if any) +## 5. Stop or start the pipeline -### Usage Tracking +```bash +curl -X POST -H "Authorization: Api-Key $API_KEY" \ + "$CONSOLE/api/v1/flow/pipelines/$PIPELINE_UUID/stop/" -Monitor your costs in real-time: -- Runtime minutes used -- Current billing rate ($0.003/minute) -- Estimated monthly cost +curl -X POST -H "Authorization: Api-Key $API_KEY" \ + "$CONSOLE/api/v1/flow/pipelines/$PIPELINE_UUID/start/" +``` -## Example: Complete Pipeline Configuration +You must stop a running pipeline before updating its config. -Here's a complete example for tracking exchange deposits: +## 6. Export the current config -```yaml -name: "exchange-deposit-tracker" -network: "mainnet" -start_ledger: "latest" - -processor: - type: "payments_memo" - config: - addresses: - - "GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" # Exchange hot wallet - min_amount: "100" - -consumer: - type: "postgres" - config: - connection_string: "postgresql://exchange:secure@db.example.com/deposits" - batch_size: 10 +```bash +curl -H "Authorization: Api-Key $API_KEY" \ + "$CONSOLE/api/v1/flow/pipelines/$PIPELINE_UUID/config/" ``` -## Querying Your Data - -Once data starts flowing, query it from PostgreSQL: - -```sql --- Find recent large payments -SELECT - transaction_hash, - source_account, - amount, - memo, - timestamp -FROM flow_data -WHERE data->>'amount' > '1000' -ORDER BY timestamp DESC -LIMIT 10; - --- Daily payment volumes -SELECT - DATE(timestamp) as day, - COUNT(*) as payment_count, - SUM((data->>'amount')::numeric) as total_volume -FROM flow_data -GROUP BY DATE(timestamp) -ORDER BY day DESC; -``` +Use export to bring a Console-created pipeline into source control. Review and remove secrets before committing. -## Next Steps +## Continuous processing -Now that your first pipeline is running: +For a continuous pipeline, omit `endLedger`: -1. **Explore More Processors**: Try [Contract Events](../processors/contract-events.md) for Soroban -2. **Add Multiple Consumers**: Send data to both PostgreSQL and Webhooks -3. **Build Complex Pipelines**: Chain processors for advanced use cases -4. **Optimize Performance**: Tune batch sizes and configurations +```yaml +spec: + network: testnet + startLedger: "2434280" + processors: + - type: contract_event + config: + network_passphrase: Test SDF Network ; September 2015 + consumers: + - type: contract_events_postgres + config: + host: postgres.example.com + port: 5432 + database: defaultdb + username: postgres + password: ${POSTGRES_PASSWORD} + sslmode: require +``` -## Common Issues +## Mainnet -### Pipeline Stuck in "Deploying" -- Check your consumer credentials -- Verify network connectivity -- Review deployment logs +For mainnet, set `spec.network` and the network passphrase: -### No Data Appearing -- Confirm transactions match your filter criteria -- Check the start ledger configuration -- Verify processor configuration +```yaml +network: mainnet +``` -### High Costs -- Optimize batch sizes for better efficiency -- Consider filtering criteria to reduce data volume -- Monitor runtime metrics +```yaml +network_passphrase: Public Global Stellar Network ; September 2015 +``` -## Getting Help +## Next steps -- **Documentation**: Browse our comprehensive guides -- **Support**: Contact support@withobsrvr.com -- **Community**: Join our Discord server -- **Status**: Check [status.withobsrvr.com](https://status.withobsrvr.com) \ No newline at end of file +- [Pipeline API reference](/docs/flow/api) +- [Pipeline concepts](/docs/flow/concepts/pipelines) +- [Processors](/docs/flow/processors/) +- [Consumers](/docs/flow/consumers/) +- [Flow pricing](/docs/flow/pricing) diff --git a/docs/flow/overview.md b/docs/flow/overview.md index e4aa551..6d063e6 100644 --- a/docs/flow/overview.md +++ b/docs/flow/overview.md @@ -3,128 +3,125 @@ sidebar_position: 1 title: Overview --- -# Flow: Data Pipeline Platform for Stellar & Soroban +# Flow -Flow is Obsrvr's data pipeline platform that provides infrastructure building blocks for processing Stellar and Soroban blockchain data. With Flow, you can deploy data processing pipelines with one click, stream blockchain data in real-time, and deliver it to your preferred destination - all without managing complex infrastructure. +Flow runs custom Stellar data pipelines on Obsrvr-managed infrastructure. Use it when Lake's standard bronze, silver, and gold tables are not the shape you need, or when you need to deliver a continuous stream into your own sink. -## What is Flow? +Most teams should start with [Obsrvr Lake](/docs/lake/overview). Use Flow when you need your own processors, filters, and destinations. -Flow enables developers to: +## What a pipeline does -- **Stream blockchain data** from Stellar and Soroban networks -- **Process data** using pre-built or custom processors -- **Deliver results** to databases, webhooks, message queues, or cloud storage -- **Pay only for what you use** with per-minute billing +A Flow pipeline reads Stellar ledger data, applies one or more processors, and writes the result to a consumer. -## Key Features - -### 🚀 One-Click Deployment -Deploy production-ready data pipelines instantly without DevOps complexity. Flow handles all the infrastructure, scaling, and orchestration for you. +```text +Stellar ledger stream + → processor: payments_memo + → consumer: PostgreSQL +``` -### 📊 Full Historical Data -Start processing from any ledger point - whether from the latest ledger or from genesis. Perfect for both real-time monitoring and historical analysis. +Processors transform records. Consumers deliver records. + +## Example pipeline shape + +```yaml +apiVersion: flow.obsrvr.com/v1 +kind: Pipeline +metadata: + name: ContractEventsToPostgres +spec: + network: testnet + startLedger: "2434280" + endLedger: "2434281" + processors: + - type: contract_event + config: + network_passphrase: Test SDF Network ; September 2015 + consumers: + - type: contract_events_postgres + config: + host: postgres.example.com + port: 5432 + connect_timeout: 30 + database: defaultdb + username: postgres + password: ${POSTGRES_PASSWORD} + sslmode: require + schema: public + table_prefix: stellar_ +``` -### 🔌 Multiple Destinations -Send processed data wherever you need it: -- **Databases**: PostgreSQL, DuckDB, SQLite, Redis -- **Streaming**: Kafka, Webhooks, ZeroMQ -- **Storage**: Amazon S3 -- **Custom**: Build your own consumer +Deploy with the Flow Pipeline API: -### 📈 Real-Time Monitoring -Track pipeline performance with: -- Live status updates -- Real-time log streaming -- Usage metrics and cost tracking -- Deployment error details +```bash +export CONSOLE="https://console.withobsrvr.com" +export API_KEY="your-team-api-key" -### 💰 Pay-As-You-Go Pricing -Simple, transparent pricing at **$0.003 per minute** of pipeline runtime: -- First 100 minutes free for new users -- No setup fees or minimum commitments -- Only pay when pipelines are running -- Monthly billing with detailed usage reports +curl -X POST \ + -H "Authorization: Api-Key $API_KEY" \ + -H "Content-Type: text/yaml" \ + --data-binary @contract-events-to-postgres.yaml \ + "$CONSOLE/api/v1/flow/pipelines/apply/?auto_start=true" +``` -## Use Cases +## When to use Flow -### Payment Processing -Track and process payments with specific memo patterns for: -- Invoice reconciliation -- Customer payment tracking -- Multi-signature payment monitoring +Use Flow when you need to: -### Contract Event Monitoring -Subscribe to Soroban smart contract events for: -- DeFi protocol monitoring -- NFT marketplace activity -- Custom dApp analytics +- push filtered Stellar activity into your own PostgreSQL database +- deliver contract events to a webhook +- run a processor that Lake does not provide yet +- keep a custom table updated continuously +- build a data product that requires your own transformation logic -### Account Balance Tracking -Monitor account balance changes for: -- Treasury management -- Wallet analytics -- Liquidity monitoring +Do not use Flow just to query standard Stellar analytics. Lake already provides token transfers, account snapshots, contract events, decoded transactions, and gold metrics. -### Network Analytics -Process network-wide data for: -- Transaction volume analysis -- Asset distribution tracking -- Network health monitoring +## Processors -## How It Works +Processors are the transformation step. Examples include: -1. **Choose Your Network**: Select between Stellar mainnet or testnet -2. **Configure Your Pipeline**: - - Select a starting ledger (latest or specific height) - - Choose processors to transform the data - - Configure consumers for data delivery -3. **Deploy**: One-click deployment to Obsrvr's infrastructure -4. **Monitor**: Track status, view logs, and monitor usage in real-time +| Processor | Use it for | +|-----------|------------| +| Payments with Memo | invoice reconciliation, exchange deposit tracking, memo-based workflows | +| Contract Events | Soroban protocol monitoring, dapp event delivery | +| Raw Transactions | custom transaction indexing | +| Account Balance | account or treasury monitoring | -## Architecture Overview +[See processors](/docs/flow/processors/). -Flow pipelines follow a simple yet powerful architecture: +## Consumers -``` -Stellar/Soroban Network → Processor(s) → Consumer(s) → Your Application -``` +Consumers write processed data to a destination. -- **Processors** transform raw blockchain data into structured formats -- **Consumers** deliver the processed data to your chosen destination -- **Orchestration** handled automatically by Flow's infrastructure +| Consumer | Use it for | +|----------|------------| +| PostgreSQL | application tables, dashboards, reporting | +| Webhook | event-driven app integration | +| Kafka | internal streams and downstream processors | +| S3 | archives and batch analytics | +| Redis | low-latency cache or queue patterns | -## Available Components +[See consumers](/docs/flow/consumers/). -### Processors -- **Payments with Memo**: Filter and process payment operations -- **Raw Transactions**: Access all network transactions -- **Account Balance**: Track balance changes -- **Contract Events**: Subscribe to Soroban events -- **Latest Ledger Metrics**: Real-time network statistics -- **SwapService**: Track DEX activity -- And more... +## Pricing -### Consumers -- **PostgreSQL**: Structured database storage -- **Webhooks**: HTTP endpoint delivery -- **Kafka**: Stream processing integration -- **Amazon S3**: Cloud storage -- **Redis**: Real-time data access -- And more... +Flow is metered by pipeline runtime. -## Getting Started +- `$0.003 per minute` of pipeline runtime +- first `100 pipeline-minutes` free for new users +- billing starts when a pipeline is running +- billing stops when a pipeline is stopped, completed, or failed -Ready to build your first pipeline? Check out our [Quickstart Guide](./getting-started/quickstart.md) to get up and running in minutes. +[See Flow pricing](/docs/flow/pricing). -## Pricing +## API access -Flow uses simple pay-as-you-go pricing: -- **$0.003 per minute** of pipeline runtime -- **First 100 minutes free** for new users -- **No setup fees** or hidden costs +Flow pipelines can be managed from Console or through the Pipeline API. The API supports validation, create, idempotent apply, update, start, stop, YAML export, registry discovery, and secrets management. -See our [Pricing Page](./pricing.md) for detailed information. +Use the API when pipeline configuration should live in source control or deploy from CI. -## Access +## Next steps -Flow is currently in early access. [Join the waitlist](https://console.withobsrvr.com) to get access when we expand availability. \ No newline at end of file +- [Create your first pipeline](/docs/flow/getting-started/quickstart) +- [Pipeline API reference](/docs/flow/api) +- [Understand pipeline concepts](/docs/flow/concepts/pipelines) +- [Query standard data with Lake instead](/docs/lake/overview) diff --git a/docs/flow/pricing.md b/docs/flow/pricing.md index 27f0cfc..ea9d28d 100644 --- a/docs/flow/pricing.md +++ b/docs/flow/pricing.md @@ -3,203 +3,146 @@ sidebar_position: 10 title: Pricing --- -# Flow Pricing - -Flow uses simple, transparent pay-as-you-go pricing that scales with your usage. Only pay for the time your pipelines are actively running. - -## Pricing Model - -### Base Rate -**$0.003 per minute** of pipeline runtime - -- No setup fees -- No minimum commitments -- No hidden costs -- Cancel anytime - -### Free Tier -**First 100 pipeline-minutes free** for new users -- Perfect for testing and development -- Automatically applied to new accounts -- No credit card required to start - -## How Billing Works - -### Runtime Calculation -- Billing starts when pipeline enters `running` state -- Billing stops when pipeline is `stopped`, `completed`, or `failed` -- Billed to the nearest minute -- Paused pipelines don't incur charges - -### Monthly Billing -- Usage aggregated monthly -- Invoiced at the end of each billing cycle -- Detailed usage reports available -- Multiple payment methods supported - -## Cost Examples - -### Small Pipeline -**Monitoring specific accounts** -- Runtime: 24/7 (43,200 minutes/month) -- Cost: $129.60/month - -### Medium Pipeline -**Processing payment streams** -- Runtime: Business hours (8h/day, 22 days) -- Runtime: 10,560 minutes/month -- Cost: $31.68/month - -### Large Pipeline -**Network-wide analytics** -- Runtime: 24/7 with 95% uptime -- Runtime: 41,040 minutes/month -- Cost: $123.12/month - -### Development Pipeline -**Testing and development** -- Runtime: 2 hours/day -- Runtime: 3,600 minutes/month -- Cost: $10.80/month - -## Cost Optimization - -### 1. Efficient Filtering -Use processor filters to reduce data volume: -```json -{ - "type": "payments_memo", - "config": { - "min_amount": "100", - "addresses": ["GSPECIFICADDRESS..."] - } -} -``` - -### 2. Batch Processing -Larger batch sizes reduce processing overhead: -```json -{ - "consumer": { - "type": "postgres", - "config": { - "batch_size": 100 // More efficient than batch_size: 1 - } - } -} -``` - -### 3. Schedule Pipelines -For non-critical data, run pipelines during specific hours: -- Process historical data in batches -- Run analytics during off-peak hours -- Pause development pipelines when not in use - -### 4. Monitor Usage -Track your usage in real-time: -- Dashboard shows current runtime -- Usage alerts available -- Detailed cost breakdowns -- Export usage data for analysis - -## Pricing Calculator - -Estimate your monthly costs: - -| Use Case | Runtime | Minutes/Month | Monthly Cost | -|----------|---------|---------------|--------------| -| Real-time monitoring | 24/7 | 43,200 | $129.60 | -| Business hours only | 8h × 22d | 10,560 | $31.68 | -| Overnight batch | 6h × 30d | 10,800 | $32.40 | -| Weekend processing | 48h × 4 | 11,520 | $34.56 | -| Hourly snapshots | 5min × 24 × 30 | 3,600 | $10.80 | - -## Subscription Management - -### Starting a Subscription -1. Add payment method in Console -2. Subscribe to Flow -3. Create your first pipeline -4. Automatic billing begins - -### Monitoring Usage -- Real-time usage dashboard -- Email notifications for thresholds -- Downloadable invoices -- Usage API for automation - -### Cancellation -- Cancel anytime from Console -- Pipelines stop at end of billing period -- Pro-rated refunds available -- Data export supported - -## Enterprise Pricing - -For high-volume usage or custom requirements: - -### Volume Discounts -- 1M+ minutes/month: Contact sales -- Annual commitments: Up to 20% discount -- Custom pricing for specific use cases - -### Enterprise Features -- Dedicated infrastructure -- SLA guarantees -- Priority support -- Custom integrations - -### Contact Sales -Email: sales@withobsrvr.com +# Pricing + +Transparent pricing for Obsrvr Lake, Flow, Gateway, and operated Stellar infrastructure. Usage-based for early builders. Flat-rate for teams running at scale. Managed plans include processor development. Every tier reads from the same platform. + +## Platform plans + +
+
+

Launch

+

Pay-as-you-go for early builders and prototypes.

+
~$99base + metered usage
+ + Start building +
+ +
+

Data Access

+

Flat rate with direct SQL access to Lake.

+
$1,800per month
+ + Contact sales +
+ +
+ Recommended +

Managed

+

Obsrvr builds and maintains custom processors for you.

+
$5,500per month
+ + Contact sales +
+ +
+

BYOC Managed

+

Run in your own cloud. We operate it.

+
$3,500+ infrastructure costs
+ + Contact sales +
+
+ +## Compare plans + +Pick by scale, not by feature gates. + +| Feature | Launch | Data Access | Managed | BYOC Managed | +|---------|--------|-------------|---------|--------------| +| Monthly | ~$99 + usage | $1,800 | $5,500 | $3,500 + infra | +| API calls | Metered | Unlimited | Unlimited | Unlimited | +| Custom processors | — | Build your own | 2–3 included | Build your own | +| Direct SQL access | — | DuckLake | DuckLake | DuckLake | +| MCP server | Shared | Dedicated | Dedicated | Dedicated | +| Support | Community | Business hours | Priority | Priority | +| SLA | — | 99.5% | 99.9% | 99.9% | + +## What each tier is for + +### Launch + +For early builders proving out an integration. Launch keeps the base commitment low while you test Lake queries, Gateway access, and Flow pipelines. + +### Data Access + +For teams that know they need decoded Stellar data and want predictable pricing. Data Access includes unlimited API calls and direct SQL access to Lake through DuckLake. + +### Managed + +For teams that need custom data products but do not want to maintain processors. Managed includes 2–3 custom processors built and maintained by Obsrvr, priority support, and a 99.9% SLA. + +### BYOC Managed + +For teams with data residency, security, or cloud-account requirements. You own the cloud account and infrastructure bill. Obsrvr deploys and operates the platform inside your perimeter. + +## Nodes + +Dedicated Stellar infrastructure for teams that need isolation, higher rate limits, or validator participation. + +| Service | Monthly | Notes | +|---------|---------|-------| +| Dedicated RPC | $799 | Isolated Stellar RPC endpoint | +| Dedicated Horizon | $1,499 | Isolated Horizon instance with unlimited throughput | +| Validator Quorum | $1,499 | Dedicated validator node with quorum set management | +| Complete Stack | $3,299 | RPC + Horizon + Validator, operated together | +| Validator maintenance-only | $599 | We operate your existing validator | + +## Compliance pricing + +Compliance plans are priced by data volume and feature scope. + +Compliance plans include dashboards, sanctions screening, counterparty analysis, and audit-ready exports. Plans are scoped to your assets and required integrations. + +[Contact sales](mailto:sales@withobsrvr.com) for compliance pricing. ## FAQ -### Is there a free trial? -Yes! First 100 pipeline-minutes are free for new users. +### What's the difference between Data Access and Managed? -### How accurate is billing? -Billing is calculated to the nearest minute with millisecond precision tracking. +Data Access gives you Lake and unlimited APIs. You build custom processors yourself. Managed adds 2–3 custom processors built and maintained by Obsrvr, plus priority support. -### Can I set spending limits? -Yes, configure spending alerts and automatic pipeline pausing in Console. +### Can I switch tiers as we scale? -### What payment methods are accepted? -- Credit/debit cards (Visa, Mastercard, Amex) -- ACH transfers (for Enterprise) -- Wire transfers (for Enterprise) +Yes. Launch → Data Access → Managed is the normal upgrade path. Usage migrates with you; the API surface does not change. -### Are there any additional fees? -No. The only cost is the per-minute runtime charge. +### How does BYOC Managed work? -### What happens if payment fails? -- 7-day grace period -- Email notifications -- Pipelines paused after grace period -- Data preserved for 30 days +You own the cloud account and pay infrastructure costs directly to AWS, GCP, or another agreed provider. Obsrvr deploys and operates the platform inside it. Your data stays inside your perimeter. -### Can I get a refund? -Pro-rated refunds available for annual plans. Contact support for assistance. +### Is the open source platform free forever? -## Comparison with Alternatives +Yes. nebu and flowctl are MIT-licensed and remain free to self-host. You pay Obsrvr when you want us to run the platform or build processors for you. -### vs. Self-Hosted Infrastructure +### Do you offer annual pricing? -| Aspect | Flow | Self-Hosted | -|--------|------|-------------| -| Setup Cost | $0 | $1000s+ | -| Monthly Cost (small) | ~$30 | ~$500+ (servers) | -| Maintenance | None | 20+ hrs/month | -| Scaling | Automatic | Manual | -| Time to Deploy | Minutes | Weeks | +Annual commitments get about 15% off on Data Access and Managed tiers. Contact sales for details. -### vs. Other Data Platforms +## Ready to move to production? -Flow's pricing is typically 50-80% less expensive than comparable platforms: -- No ingress/egress fees -- No storage charges -- No per-event pricing -- Simple per-minute model +Most teams are live within a week of access. Start pay-as-you-go today or contact sales for committed plans. -## Getting Started - -Ready to start? [Create your first pipeline](./getting-started/quickstart.md) and get 100 free minutes! \ No newline at end of file +
+ Request access + Contact sales +
diff --git a/docs/flow/processors/index.md b/docs/flow/processors/index.md index 66da7c8..93f5314 100644 --- a/docs/flow/processors/index.md +++ b/docs/flow/processors/index.md @@ -1,761 +1,239 @@ --- sidebar_position: 1 -title: Processors Overview +title: Processors Reference --- -# Processors +# Flow processors -Processors are the core components that transform raw blockchain data into structured, actionable information. Each processor is designed for specific use cases and data types on the Stellar and Soroban networks. +This page lists the processor IDs currently exposed by the Flow registry. Use these IDs in `spec.processors[].type` when creating pipelines through the API. -## Available Processors +```bash +export CONSOLE="https://console.withobsrvr.com" +export API_KEY="your-team-api-key" -### Core Ledger & Transaction - -#### Ledger Reader -Reads ledger close metadata and emits individual transactions. - -**Configuration:** -```yaml -type: ledger_reader -config: - network_passphrase: "Public Global Stellar Network ; September 2015" +curl -H "Authorization: Api-Key $API_KEY" \ + "$CONSOLE/api/v1/flow/registry/processors/" ``` -**Use Case:** Entry point for transaction-level processing pipelines - ---- - -#### Passthrough Processor -Passes ledger data through with minimal transformation, maintains XDR structure. - -**Configuration:** -```yaml -type: passthrough -config: - network_passphrase: "Public Global Stellar Network ; September 2015" - add_metadata: false - include_full_xdr: true - include_xdr_json: false -``` +## Processor summary -**Use Case:** Preserve raw ledger data while adding lightweight metadata +| ID | Name | Description | Compatible consumers | +|----|------|-------------|----------------------| +| `payments_memo` | Payments with Memo | Process payment operations with memo fields | None currently exposed by registry compatibility rules | +| `raw_transactions` | Raw Transactions | Process raw transactions | None currently exposed by registry compatibility rules | +| `account_balance` | Account Balance | Track account balances | None currently exposed by registry compatibility rules | +| `latest_ledger` | Latest Ledger Metrics | Process and track metrics for the latest ledger | `save_latest_ledger_redis` | +| `contract_event` | Contract Event | Process Soroban contract events | `contract_events_postgres` | +| `soroswap` | SwapService | Process SwapService events | `soroswap_postgres` | +| `account_data` | Account Data | Process Stellar account data changes | `account_data_postgres` | +| `contract_invocation` | Contract Invocation | Track Soroban contract invocations and their details | `contract_invocations_postgres`, `extracted_contract_invocations_postgres` | +| `contract_filter` | Contract Filter | Filter events by specific contract IDs | `contract_events_postgres`, `contract_invocations_postgres`, `extracted_contract_invocations_postgres` | +| `contract_data` | Contract Data | Process Stellar Soroban contract data changes | `contract_data_postgres` | +| `extracted_contract_invocation` | Extracted Contract Invocation | Extract structured business data from contract invocations using configurable schemas | `extracted_contract_invocations_postgres` | +| `event_payment_extractor` | Event Payment Extractor | Extracts structured payment events from contract events (Kwickbit payment processor) | `event_payment_postgres`, `google_pubsub_v2` | ---- +## Processor configuration reference -#### Ledger to JSON -Converts LedgerCloseMeta to JSON format. +### `payments_memo` — Payments with Memo -**Configuration:** -```yaml -type: ledger_to_json -config: - network_passphrase: "Public Global Stellar Network ; September 2015" -``` +Process payment operations with memo fields -**Use Case:** Export ledger data as JSON for downstream systems +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `min_amount` | string | No | | Minimum payment amount to process | +| `memo_text` | string | No | `internal` | Filter payments by memo text | +| `asset_code` | string | No | | Filter by specific asset | +| `addresses` | array | No | | Filter by specific addresses | ---- -#### Ledger Changes -Extracts and processes ledger entry changes (creates, updates, deletes). +**Example** -**Configuration:** ```yaml -type: ledger_changes -config: - network_passphrase: "Public Global Stellar Network ; September 2015" +processors: + - type: payments_memo + config: + min_amount: "" + memo_text: "internal" + asset_code: "" ``` -**Use Case:** Track state changes in the ledger +### `raw_transactions` — Raw Transactions ---- - -#### Latest Ledger -Tracks and emits latest ledger information with transaction metrics. - -**Configuration:** -```yaml -type: latest_ledger -``` +Process raw transactions -**Use Case:** Monitor ledger progression and transaction activity +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `transaction_types` | array | No | | Types of transactions to process | ---- -#### Latest Ledger RPC -Transforms raw getLatestLedger RPC data into structured messages. +**Example** -**Configuration:** ```yaml -type: latest_ledger_rpc -config: - network_passphrase: "Public Global Stellar Network ; September 2015" +processors: + - type: raw_transactions ``` -**Use Case:** Process RPC responses for latest ledger queries +### `account_balance` — Account Balance ---- +Track account balances -#### Operation Processor -Extracts all operation types from transactions. +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `account_ids` | array | Yes | | List of account IDs to monitor | +| `assets` | array | No | | Filter by specific assets | -**Configuration:** -```yaml -type: operation -config: - network_passphrase: "Public Global Stellar Network ; September 2015" -``` -**Use Case:** Track all operations regardless of type +**Example** ---- - -### Account Monitoring - -#### Account Data -Process Stellar account data changes including creation/updates/deletion. - -**Configuration:** ```yaml -type: account_data -config: - network_passphrase: "Public Global Stellar Network ; September 2015" +processors: + - type: account_balance + config: + account_ids: [] ``` -**Use Case:** Track account state changes, balance updates - ---- +### `latest_ledger` — Latest Ledger Metrics -#### Account Data Filter -Filters account records by various criteria. +Process and track metrics for the latest ledger -**Configuration:** -```yaml -type: account_data_filter -config: - account_ids: - - "GABC..." - - "GDEF..." - min_balance: "100000000" - change_types: - - "created" - - "updated" - start_date: "2024-01-01T00:00:00Z" - end_date: "2024-12-31T23:59:59Z" -``` +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `network_passphrase` | string | Yes | | The network passphrase for the Stellar network | -**Use Case:** Filter account changes for specific accounts or thresholds - ---- -#### Account Effects -Extracts effects (side-effects of operations) for accounts. +**Example** -**Configuration:** ```yaml -type: account_effects -config: - account: "GABC123..." - network_passphrase: "Public Global Stellar Network ; September 2015" +processors: + - type: latest_ledger + config: + network_passphrase: "Test SDF Network ; September 2015" ``` -**Use Case:** Track all effects on specific accounts (credits, debits, trustlines) +### `contract_event` — Contract Event ---- +Process Soroban contract events -#### Account Transactions -Extracts transactions involving specific accounts (Stellar & Soroban). +No processor-specific configuration fields. -**Configuration:** -```yaml -type: account_transactions -config: - account: "GABC123..." - network_passphrase: "Public Global Stellar Network ; September 2015" -``` -**Use Case:** Transaction history for accounts +**Example** ---- - -#### Account Year Analytics -Generates yearly analytics for a specific account. - -**Configuration:** ```yaml -type: account_year_analytics -config: - account_id: "GABC123..." - year: 2024 - network_passphrase: "Public Global Stellar Network ; September 2015" +processors: + - type: contract_event ``` -**Use Case:** Annual account activity reports +### `soroswap` — SwapService ---- +Process SwapService events -#### Create Account -Extracts create account operations. - -**Configuration:** -```yaml -type: create_account -config: - network_passphrase: "Public Global Stellar Network ; September 2015" -``` - -**Use Case:** Track new account creation events - ---- +No processor-specific configuration fields. -### Soroban Contract Processing -#### [Contract Events](./contract-events.md) -Captures and processes events emitted by Soroban smart contracts. +**Example** -**Configuration:** ```yaml -type: contract_events -config: - network_passphrase: "Public Global Stellar Network ; September 2015" +processors: + - type: soroswap ``` -**Use Case:** DeFi protocol monitoring, dApp event tracking +### `account_data` — Account Data ---- +Process Stellar account data changes -#### Contract Invocation -Extracts contract invocations with full execution details (arguments, results, state changes). +No processor-specific configuration fields. -**Configuration:** -```yaml -type: contract_invocation -config: - network_passphrase: "Public Global Stellar Network ; September 2015" -``` -**Use Case:** Comprehensive contract execution tracking - ---- +**Example** -#### Contract Filter -Filters events by specific contract IDs. - -**Configuration:** ```yaml -type: contract_filter -config: - contract_ids: - - "CCABC123..." - - "CCDEF456..." +processors: + - type: account_data ``` -**Use Case:** Targeted monitoring of specific contracts +### `contract_invocation` — Contract Invocation ---- +Track Soroban contract invocations and their details -#### Contract Data -Processes contract data changes on the Soroban network. +No processor-specific configuration fields. -**Configuration:** -```yaml -type: contract_data -config: - network_passphrase: "Public Global Stellar Network ; September 2015" -``` -**Use Case:** State change monitoring, contract storage analytics +**Example** ---- - -#### Contract Creation -Tracks contract creation events. - -**Configuration:** ```yaml -type: contract_creation -config: - network_passphrase: "Public Global Stellar Network ; September 2015" +processors: + - type: contract_invocation ``` -**Use Case:** Monitor new contract deployments +### `contract_filter` — Contract Filter ---- +Filter events by specific contract IDs -#### Filtered Contract Invocation -Filters contract invocations by criteria. +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `contract_ids` | array | Yes | | List of contract IDs to filter events for | -**Configuration:** -```yaml -type: filtered_contract_invocation -config: - contract_id: "CCABC123..." - function_name: "transfer" -``` - -**Use Case:** Filter invocations by contract ID or function name - ---- -#### Stellar Contract Events -Processes Stellar Asset Contract (SAC) events. +**Example** -**Configuration:** ```yaml -type: stellar_contract_events +processors: + - type: contract_filter + config: + contract_ids: ["CC..."] ``` -**Use Case:** Track Stellar Asset Contract interactions +### `contract_data` — Contract Data ---- - -#### Get Events RPC -Fetches events via RPC getEvents endpoint. - -**Configuration:** -```yaml -type: get_events_rpc -config: - rpc_url: "https://rpc.nodeswithobsrvr.co/" -``` +Process Stellar Soroban contract data changes -**Use Case:** Query historical contract events via RPC +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `network_passphrase` | string | Yes | | Stellar network identifier | +| `name` | string | No | `contract_data_processor` | Optional processor identifier | ---- -#### Filter Events -Filters contract events by various criteria. +**Example** -**Configuration:** ```yaml -type: filter_events -config: - event_type: "contract" - contract_ids: - - "CCABC123..." +processors: + - type: contract_data + config: + network_passphrase: "Test SDF Network ; September 2015" + name: "contract_data_processor" ``` -**Use Case:** Filter events by type, topic, or contract +### `extracted_contract_invocation` — Extracted Contract Invocation ---- +Extract structured business data from contract invocations using configurable schemas -### Bronze Layer (Hubble-Compatible) +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `extraction_schemas` | object | Yes | `{}` | Schema definitions for extracting business data from contract invocations | +| `name` | string | No | `contract_invocation_extractor` | Optional processor identifier | -#### Bronze Extractors -Extracts ALL 19 Bronze table data types from LedgerCloseMeta (Hubble-compatible schema). -**Configuration:** -```yaml -type: bronze_extractors -config: - network_passphrase: "Test SDF Network ; September 2015" -``` - -**Output:** ledgers_row_v2, transactions_row_v2, operations_row_v2, effects_row_v1, trades_row_v1, accounts_snapshot_v1, trustlines_snapshot_v1, contract_events_stream_v1, and 11 more - -**Use Case:** Export all data for medallion architecture - ---- - -#### Bronze to Contract Invocation -Converts Bronze SQL query results to ContractInvocation format. - -**Configuration:** -```yaml -type: bronze_to_contract_invocation -``` - -**Use Case:** Bridge Bronze data queries with contract invocation extractors - ---- - -### Payment & Operation Processing - -#### Filter Payments -Filters payment operations by amount and asset. - -**Configuration:** -```yaml -type: filter_payments -config: - min_amount: "100" - asset_code: "USDC" - network_passphrase: "Public Global Stellar Network ; September 2015" -``` - -**Use Case:** Track large payments or specific asset transfers - ---- - -#### Participant Extractor -Identifies all accounts involved in a transaction. - -**Configuration:** -```yaml -type: participant_extractor -``` - -**Use Case:** Extract transaction participants for network analysis - ---- - -#### Event Payment Extractor -Extracts structured EventPayment data from contract events. - -**Configuration:** -```yaml -type: event_payment_extractor -``` - -**Use Case:** Parse payment events from payment processor contracts - ---- - -#### Transaction XDR Extractor -Extracts transaction XDR data. +**Example** -**Configuration:** ```yaml -type: transaction_xdr_extractor +processors: + - type: extracted_contract_invocation + config: + extraction_schemas: {} + name: "contract_invocation_extractor" ``` -**Use Case:** Export transaction XDR for debugging or archival +### `event_payment_extractor` — Event Payment Extractor ---- - -### Asset & Market Data - -#### Asset Processor -Processes asset events from operations. - -**Configuration:** -```yaml -type: asset -``` - -**Use Case:** Track asset usage in payments and trades - ---- +Extracts structured payment events from contract events (Kwickbit payment processor) -#### Asset Enrichment -Enriches asset data with issuer account information. +No processor-specific configuration fields. -**Configuration:** -```yaml -type: asset_enrichment -config: - connection_string: "postgresql://user:pass@host:5432/database" - network_passphrase: "Public Global Stellar Network ; September 2015" -``` - -**Use Case:** Add issuer metadata (home domain, auth flags) to asset data - ---- - -#### Transform to Asset Stats -Generates asset statistics. - -**Configuration:** -```yaml -type: transform_to_asset_stats -``` - -**Use Case:** Asset analytics and metrics (holder counts, volume) - ---- - -#### Transform to Token Price -Calculates token prices. - -**Configuration:** -```yaml -type: transform_to_token_price -``` - -**Use Case:** Price tracking for assets - ---- - -#### Transform to Market Cap -Calculates market cap and supply data. - -**Configuration:** -```yaml -type: transform_to_market_cap -``` - -**Use Case:** Market cap tracking for assets - ---- -#### Transform to Market Analytics -Generates market analytics. +**Example** -**Configuration:** ```yaml -type: transform_to_market_analytics +processors: + - type: event_payment_extractor ``` - -**Use Case:** Comprehensive market analysis - ---- - -#### Market Metrics -Generates sliding window market metrics. - -**Configuration:** -```yaml -type: market_metrics -``` - -**Use Case:** Trading pair analytics with time-series data - ---- - -#### Transform to Ticker Asset -Transforms operations into ticker asset data. - -**Configuration:** -```yaml -type: transform_to_ticker_asset -``` - -**Use Case:** Asset ticker data for exchanges - ---- - -#### Transform to Ticker Orderbook -Generates orderbook ticker data. - -**Configuration:** -```yaml -type: transform_to_ticker_orderbook -``` - -**Use Case:** Orderbook snapshots for trading pairs - ---- - -### DeFi Protocol Processors - -#### Soroswap -Processes Soroswap DEX events (new pairs, syncs). - -**Configuration:** -```yaml -type: soroswap -``` - -**Use Case:** Track Soroswap liquidity pool creation and state changes - ---- - -#### Soroswap Router -Processes Soroswap router events. - -**Configuration:** -```yaml -type: soroswap_router -``` - -**Use Case:** Track Soroswap swap routing - ---- - -#### Phoenix AMM -Processes Phoenix AMM contract events. - -**Configuration:** -```yaml -type: phoenix_amm -``` - -**Use Case:** Track Phoenix AMM swaps and liquidity events - ---- - -#### Kale -Processes Kale contract events and invocations. - -**Configuration:** -```yaml -type: kale -``` - -**Use Case:** Track Kale protocol metrics - ---- - -### Application Transforms - -#### Transform to App Account -Transforms account data for application use. - -**Configuration:** -```yaml -type: transform_to_app_account -``` - -**Use Case:** Application-specific account data formatting - ---- - -#### Transform to App Metrics -Generates application network metrics. - -**Configuration:** -```yaml -type: transform_to_app_metrics -``` - -**Use Case:** Network-wide metrics for applications - ---- - -#### Transform to App Payment -Transforms payments for application use. - -**Configuration:** -```yaml -type: transform_to_app_payment -``` - -**Use Case:** Application-friendly payment formatting - ---- - -#### Transform to App Trade -Transforms trades for application use. - -**Configuration:** -```yaml -type: transform_to_app_trade -``` - -**Use Case:** Application-friendly trade formatting - ---- - -#### Transform to App Trustline -Transforms trustlines for application use. - -**Configuration:** -```yaml -type: transform_to_app_trustline -``` - -**Use Case:** Application-friendly trustline formatting - ---- - -### State & Effects - -#### Stellar Effects -Processes Stellar effects. - -**Configuration:** -```yaml -type: stellar_effects -``` - -**Use Case:** Extract Horizon-style effects from ledgers - ---- - -#### Claimable Balance -Processes claimable balance entries. - -**Configuration:** -```yaml -type: claimable_balance -config: - network_passphrase: "Public Global Stellar Network ; September 2015" -``` - -**Use Case:** Track claimable balance creation/updates/claims - ---- - -### Wallet Backend - -#### Wallet Backend -Processes state changes for wallet backend. - -**Configuration:** -```yaml -type: wallet_backend -``` - -**Use Case:** Track all state changes relevant to wallet applications - ---- - -### Utilities - -#### Blank Processor -Example/template processor for payments. - -**Configuration:** -```yaml -type: blank -config: - network_passphrase: "Public Global Stellar Network ; September 2015" -``` - -**Use Case:** Template for building custom processors - ---- - -#### Stdout Sink -Writes payload to stdout (terminal sink). - -**Configuration:** -```yaml -type: stdout_sink -``` - -**Use Case:** Debug output, pipe to other tools - ---- - -## Choosing the Right Processor - -Consider these factors when selecting a processor: - -1. **Data Requirements**: What specific blockchain data do you need? -2. **Performance**: Some processors are more resource-intensive than others -3. **Filtering Needs**: Many processors offer built-in filtering options -4. **Output Format**: Each processor outputs data in a specific structure - -## Processor Configuration - -All processors support configuration through the Flow pipeline builder. Common configuration patterns include: - -```json -{ - "processor_type": "payments_memo", - "config": { - "memo_text": "invoice-2024", - "min_amount": "100", - "asset_code": "USDC" - } -} -``` - -## Combining Processors - -You can chain multiple processors in a single pipeline for complex data processing scenarios. For example: -- Use Contract Filter → Contract Events for targeted event monitoring -- Combine Account Balance → Latest Ledger for balance snapshots - -## Performance Considerations - -- **Filtered processors** (like Payments with Memo) are more efficient than processing all data -- **Account-specific processors** scale better than network-wide processors -- **Contract processors** require Soroban-enabled networks - -## Next Steps - -- Explore individual processor documentation for detailed configuration options -- Learn about [consumers](../consumers/) to deliver your processed data -- Check our [Getting Started Guide](../getting-started/quickstart.md) for hands-on examples \ No newline at end of file diff --git a/docs/gateway/overview.md b/docs/gateway/overview.md index d0df231..dfa20a9 100644 --- a/docs/gateway/overview.md +++ b/docs/gateway/overview.md @@ -3,90 +3,90 @@ sidebar_position: 1 title: Gateway Overview --- -# Obsrvr Gateway Services +# Obsrvr Gateway -Obsrvr Gateway provides enterprise-grade access to Stellar and Soroban networks through managed API endpoints. Whether you need Horizon API access for Stellar or JSON-RPC for Soroban smart contracts, our gateway services offer reliability, scalability, and simplicity. +Gateway provides authenticated access to Stellar Horizon, Stellar RPC, and Lake APIs through a single Gateway URL. Use it when your application needs SDK-compatible network access or one entrance point for Obsrvr services. -## What is Obsrvr Gateway? +Gateway is infrastructure. If you need decoded transfers, contract analytics, or account snapshots, start with [Obsrvr Lake](/docs/lake/overview). -Obsrvr Gateway is a fully managed service that provides: +## Base URL -- **Stellar Horizon API** endpoints for mainnet and testnet -- **Soroban RPC** endpoints for smart contract interactions -- **High availability** with global infrastructure -- **No rate limits** for authenticated users -- **Full historical data** access +Use `https://gateway.withobsrvr.com` for Gateway services. -## Available Endpoints +| Service | Mainnet | Testnet | +|---------|---------|---------| +| Horizon | `https://gateway.withobsrvr.com/horizon/mainnet/` | `https://gateway.withobsrvr.com/horizon/testnet/` | +| Stellar RPC | `https://gateway.withobsrvr.com/rpc/mainnet/` | `https://gateway.withobsrvr.com/rpc/testnet/` | +| Lake API | `https://gateway.withobsrvr.com/lake/v1/mainnet` | `https://gateway.withobsrvr.com/lake/v1/testnet` | -### Stellar Horizon API +Gateway routes by network in the URL path: -Access the complete Stellar network through our Horizon endpoints: +- Horizon: `/horizon/{network}/...` +- Stellar RPC: `/rpc/{network}/` +- Lake: `/lake/v1/{network}/...` -#### Mainnet -``` -https://stellar.nodeswithobsrvr.co/ -``` +Supported networks depend on your Gateway plan and configuration. Common values are `mainnet` and `testnet`. -#### Testnet -``` -https://stellar-testnet.nodeswithobsrvr.co/ +## Authentication + +Send your Obsrvr API key in the `Authorization` header. + +```bash +Authorization: Api-Key $API_KEY ``` -### Soroban RPC +Create and rotate keys in [Console](https://console.withobsrvr.com). Keep API keys server-side and do not embed them in browser code or public repositories. -Interact with Soroban smart contracts through our RPC endpoints: +## Horizon through Gateway -#### Mainnet -``` -https://rpc.nodeswithobsrvr.co/ -``` +Horizon requests use normal Horizon paths after `/horizon/{network}`. -#### Testnet -``` -https://rpc-testnet.nodeswithobsrvr.co/ +```bash +export API_KEY="your-api-key" +export HORIZON="https://gateway.withobsrvr.com/horizon/testnet" + +curl -H "Authorization: Api-Key $API_KEY" \ + "$HORIZON/ledgers?limit=1&order=desc" ``` -## Key Features +Account example: -### 🚀 Instant Access -- No infrastructure setup required -- Start making API calls immediately -- Full historical data available +```bash +curl -H "Authorization: Api-Key $API_KEY" \ + "$HORIZON/accounts/GAIH3ULLFQ4DGSECF2AR555KZ4KNDGEKN4AFI4SU2M7B43MGK3QJZNSR" +``` -### 🔒 Enterprise Security -- TLS encryption for all connections -- API key authentication -- IP allowlisting available +Gateway is compatible with Stellar SDKs that accept a Horizon URL and custom headers. -### 📊 Reliability -- 99.9% uptime SLA -- Global load balancing -- Automatic failover +```javascript +import { Horizon } from '@stellar/stellar-sdk'; -### 🔧 Developer Friendly -- Compatible with all Stellar SDKs -- Comprehensive API documentation -- WebSocket support for streaming +const server = new Horizon.Server('https://gateway.withobsrvr.com/horizon/testnet', { + headers: { + Authorization: `Api-Key ${process.env.OBSRVR_API_KEY}`, + }, +}); -## Getting Started +const ledgers = await server.ledgers().order('desc').limit(1).call(); +console.log(ledgers.records[0]); +``` -### 1. Sign Up +For mainnet, use: -Create an account at [console.withobsrvr.com](https://console.withobsrvr.com) to get your API key. +```text +https://gateway.withobsrvr.com/horizon/mainnet +``` -### 2. Make Your First Request +## Stellar RPC through Gateway -#### Horizon API Example -```bash -curl -H "Authorization: Bearer YOUR_API_KEY" \ - https://stellar.nodeswithobsrvr.co/accounts/GABC...XYZ -``` +Stellar RPC requests are JSON-RPC `POST` requests to `/rpc/{network}/`. -#### Soroban RPC Example ```bash -curl -X POST https://rpc.nodeswithobsrvr.co/ \ - -H "Authorization: Bearer YOUR_API_KEY" \ +export API_KEY="your-api-key" +export RPC="https://gateway.withobsrvr.com/rpc/testnet/" + +curl -X POST "$RPC" \ + -H "Authorization: Api-Key $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", @@ -95,116 +95,93 @@ curl -X POST https://rpc.nodeswithobsrvr.co/ \ }' ``` -### 3. Integrate with SDKs +JavaScript `fetch` example: -#### JavaScript/TypeScript ```javascript -const StellarSdk = require('stellar-sdk'); - -const server = new StellarSdk.Server('https://stellar.nodeswithobsrvr.co/', { +const response = await fetch('https://gateway.withobsrvr.com/rpc/testnet/', { + method: 'POST', headers: { - 'Authorization': 'Bearer YOUR_API_KEY' - } + Authorization: `Api-Key ${process.env.OBSRVR_API_KEY}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + jsonrpc: '2.0', + id: 1, + method: 'getHealth', + }), }); -``` - -#### Python -```python -from stellar_sdk import Server -server = Server( - horizon_url="https://stellar.nodeswithobsrvr.co/", - headers={"Authorization": "Bearer YOUR_API_KEY"} -) +console.log(await response.json()); ``` -## Use Cases +Stellar SDK RPC example: -### DeFi Applications -- Real-time price feeds -- Liquidity pool monitoring -- Transaction history tracking - -### Wallets -- Balance queries -- Transaction submission -- Payment streaming - -### Analytics Platforms -- Network statistics -- Asset distribution analysis -- Historical data queries +```javascript +import { rpc } from '@stellar/stellar-sdk'; -### NFT Marketplaces -- Asset issuance -- Ownership tracking -- Metadata management +const server = new rpc.Server('https://gateway.withobsrvr.com/rpc/testnet/', { + headers: { + Authorization: `Api-Key ${process.env.OBSRVR_API_KEY}`, + }, +}); -## Advanced Features +const health = await server.getHealth(); +console.log(health); +``` -### Streaming -Real-time updates via Server-Sent Events: +For mainnet, use: -```javascript -server.transactions() - .cursor('now') - .stream({ - onmessage: (transaction) => { - console.log('New transaction:', transaction); - } - }); +```text +https://gateway.withobsrvr.com/rpc/mainnet/ ``` -### Pagination -Efficiently navigate large datasets: +Use Stellar RPC for contract simulation, transaction submission, ledger metadata, and RPC methods used by Stellar SDKs. -```javascript -const payments = await server.payments() - .forAccount(accountId) - .limit(50) - .order('desc') - .call(); -``` +## Lake through Gateway -### Filtering -Query specific data subsets: +Lake is exposed through Gateway under `/lake/v1/{network}`. -```javascript -const trades = await server.trades() - .forAssetPair(assetA, assetB) - .limit(100) - .call(); -``` +```bash +export API_KEY="your-api-key" +export BASE="https://gateway.withobsrvr.com/lake/v1/testnet" -## Comparison with Self-Hosted +curl -H "Authorization: Api-Key $API_KEY" \ + "$BASE/api/v1/silver/contracts/top?period=24h&limit=10" +``` -| Feature | Obsrvr Gateway | Self-Hosted | -|---------|---------------|-------------| -| Setup Time | Instant | Days/Weeks | -| Maintenance | None | Continuous | -| Cost | Pay-per-use | Infrastructure + Staff | -| Scalability | Automatic | Manual | -| Historical Data | Full | Limited by storage | -| High Availability | Built-in | Complex setup | +## Migrating from old direct endpoints -## Pricing +If your app still uses the older service-specific hostnames, update them to the Gateway URL path format. -Gateway services are included with your Obsrvr subscription. See our [pricing page](https://withobsrvr.com/pricing) for details. +| Old style | New Gateway style | +|-----------|-------------------| +| `https://stellar.nodeswithobsrvr.co/...` | `https://gateway.withobsrvr.com/horizon/mainnet/...` | +| `https://stellar-testnet.nodeswithobsrvr.co/...` | `https://gateway.withobsrvr.com/horizon/testnet/...` | +| `https://rpc.nodeswithobsrvr.co/` | `https://gateway.withobsrvr.com/rpc/mainnet/` | +| `https://rpc-testnet.nodeswithobsrvr.co/` | `https://gateway.withobsrvr.com/rpc/testnet/` | -## Guides +The request shape stays the same for Horizon paths and RPC JSON bodies. Only the base URL changes. -Learn how to leverage gateway services for specific use cases: +## When to use Gateway vs Lake -- [Running Stellar RPC with Full History](./guides/stellar-rpc-full-history.md) - Deploy your own RPC with cloud storage +| Need | Use | +|------|-----| +| Submit a transaction | Gateway RPC or Horizon | +| Call a Stellar SDK method | Gateway RPC or Horizon | +| Query account balances as a wallet would | Gateway Horizon | +| Query normalized token transfers | Lake | +| Find active Soroban contracts | Lake | +| Build compliance reports | Lake gold endpoints | -## Support +## Operational notes -- **Documentation**: Full API reference at [developers.stellar.org](https://developers.stellar.org) -- **Discord**: Join our community for real-time help -- **Email**: support@withobsrvr.com +- Use testnet endpoints for examples and development. +- Keep API keys server-side. Do not embed keys in browser code or public repos. +- Generate separate keys for production and development. +- If you need plan-specific limits or uptime terms, check your Console subscription or contact Obsrvr support. -## Next Steps +## Next steps -- [Create an account](https://console.withobsrvr.com) to get started -- Explore our [Flow pipelines](/docs/flow/overview) for data processing -- Check our [status page](https://status.withobsrvr.com) for real-time monitoring \ No newline at end of file +- [Query decoded Stellar data with Lake](/docs/lake/overview) +- [Run the Lake quickstart](/docs/lake/getting-started/quickstart) +- [Create custom pipelines with Flow](/docs/flow/overview) diff --git a/docs/intro.md b/docs/intro.md index 0f5968d..2f5a5d8 100644 --- a/docs/intro.md +++ b/docs/intro.md @@ -2,98 +2,92 @@ sidebar_position: 1 --- -# Introduction +# What is Obsrvr? -## **Obsrvr Gateway Overview** +Obsrvr is the data backbone for Stellar builders. It gives developers, analysts, and compliance teams decoded ledger data, semantic tables, and production APIs without making them run Horizon, parse XDR, index Soroban events, or maintain their own warehouse. -Obsrvr Gateway provides a seamless and secure connection to both the **Stellar** and **Soroban RPC** networks. It allows developers and businesses to interact with both **Mainnet** and **Testnet** environments using dedicated APIs. Whether you are building decentralized applications (dApps) or exploring transaction data, the Obsrvr Gateway ensures easy access to the tools you need. +Obsrvr is not a generic Web3 data platform. It is built around Stellar's data model: ledgers, operations, trustlines, path payments, Soroban contract events, SAC transfers, and account state. -### **Supported Networks:** +## Start with Lake -- **Stellar Mainnet:** `https://stellar.nodeswithobsrvr.co/` -- **Stellar Testnet:** `https://stellar-testnet.nodeswithobsrvr.co/` -- **Soroban RPC Mainnet:** `https://rpc.nodeswithobsrvr.co/` -- **Soroban RPC Testnet:** `https://rpc-testnet.nodeswithobsrvr.co/` +Obsrvr Lake is the centerpiece. It stores Stellar data in a medallion architecture: -These endpoints allow access to their respective networks for interacting with accounts, assets, transactions, and other key data on the Stellar and Soroban networks. +| Layer | What it contains | Use it for | +|-------|------------------|------------| +| Bronze | Raw decoded Stellar ledger data | Audit trails, custom derivations, parity with ledger history | +| Silver | Normalized analytics tables | Token transfers, account snapshots, contract events, Soroban calls | +| Gold | Business-ready metrics | Stablecoin volume, fee intelligence, network health, compliance exports | ---- +Most teams should start with Lake. If you want USDC transfers for an account, top Soroban contracts, account balances at a point in time, or a transaction summary, Lake is the shortest path. -## **API Key Authentication** +```bash +export API_KEY="your-api-key" +export BASE="https://gateway.withobsrvr.com/lake/v1/testnet" -To access any of the provided APIs, you must authenticate using an API key. These keys can be generated from the **Obsrvr Console**. +curl -H "Authorization: Api-Key $API_KEY" \ + "$BASE/api/v1/silver/transfers?asset_code=USDC&limit=10" +``` -### **How to Generate an API Key:** +## Product map -1. Visit the Obsrvr Console: [console.withobsrvr.com](https://console.withobsrvr.com). -2. Create a subscription to access Obsrvr services. -3. After creating your subscription, navigate to the "Teams" section. -4. Generate a **Team API Key**. -5. Your API key will be displayed at the top of the screen in a message bar. Make sure to copy it for future use. -6. You can revoke the API key at any time by selecting the **Revoke** button next to the key in the Console. +```text +Console + └─ Manage API keys, subscriptions, and resources -### **Using the API Key in Requests:** +Gateway + ├─ Horizon access + ├─ Stellar RPC access + └─ Lake API entrance point -When making requests to the Obsrvr Gateway, include the API key in the HTTP headers for authentication. The format for the header is as follows: +Lake + ├─ Bronze: raw decoded ledger data + ├─ Silver: analytics-ready Stellar tables + └─ Gold: business-ready metrics and compliance outputs -```bash -Authorization: Api-Key YOUR_API_KEY +Flow + └─ Managed custom pipelines for teams that need their own processors and sinks ``` -**Example Request:** +## When to use each product -```bash -curl -H "Authorization: Api-Key 1bBhBBbB.AAaa6MlgbAa5CCCIQaaaaCRClP4567yy" -L https://stellar-testnet.nodeswithobsrvr.co/ -``` +### Lake -This header ensures that your requests are authorized to access the network resources. +Use Lake when you need decoded, queryable Stellar data. Lake is the default choice for wallets, explorers, analysts, compliance workflows, and dashboards. ---- +Start here if your question sounds like: -## **Endpoints** +- What token transfers involved this account? +- Which Soroban contracts were most active in the last 24 hours? +- What was this account's balance at a ledger or timestamp? +- What events did this contract emit? +- What stablecoin volume moved yesterday? -### **Stellar Mainnet:** +[Open Lake docs](/docs/lake/overview). -- **Base URL:** `https://stellar.nodeswithobsrvr.co/` -- **Usage:** Access Stellar’s main network for real-time transactions, accounts, and asset data. +### Flow -### **Stellar Testnet:** +Use Flow when you need a custom managed pipeline. Flow runs pipelines made from processors and consumers: read from Stellar, transform records, and write to PostgreSQL, webhooks, Kafka, S3, or another sink. -- **Base URL:** `https://stellar-testnet.nodeswithobsrvr.co/` -- **Usage:** Interact with Stellar’s test network to develop and test applications. +Use Flow when Lake's standard tables are not the shape you need, or when you need to deliver events into your own system continuously. -### **Soroban RPC Mainnet:** +[Open Flow docs](/docs/flow/overview). -- **Base URL:** `https://rpc.nodeswithobsrvr.co/` -- **Usage:** Execute Soroban smart contracts, and query real-time blockchain data on the main network. +### Gateway -### **Soroban RPC Testnet:** +Use Gateway for direct Horizon and Stellar RPC access. It is SDK-compatible infrastructure for network calls, transaction submission, and RPC methods. -- **Base URL:** `https://rpc-testnet.nodeswithobsrvr.co/` -- **Usage:** Develop, test, and simulate Soroban smart contracts in a test environment. +Gateway is useful, but it is not the main reason to choose Obsrvr. Lake is where decoded Stellar data becomes queryable. ---- - -## **Managing API Keys** - -API keys are critical for accessing and managing Obsrvr Gateway resources. You can create, view, and revoke keys directly through the Obsrvr Console. +[Open Gateway docs](/docs/gateway/overview). -- **Creating API Keys:** - - Go to the Obsrvr Console and navigate to the "Teams" section. - - Click on "New Team API Key" to generate a key. -- **Revoking API Keys:** - - To revoke an API key, select the **Revoke** button next to the specific key in the Console. Once revoked, the key will no longer be valid for authorization. +### Console ---- - -## **Best Practices** +Use Console to manage Obsrvr resources: API keys, subscriptions, Gateway access, Flow pipelines, and account settings. -- **Keep your API key private**: Ensure that your API keys are not shared publicly or in your source code. -- **Use a separate key for each environment**: You can generate separate API keys for different teams or environments (production, testing) to maintain better security and manageability. -- **Rotate your keys regularly**: Regularly rotating API keys ensures enhanced security and minimizes risk in case of accidental exposure. +[Open Console](https://console.withobsrvr.com). ---- +## What makes Obsrvr different -### **Next Steps:** +Stellar's RPC and Horizon give you network access. Hubble-style datasets give you raw tables. `getEvents` gives you contract events, but not a complete semantic model for transfers, balances, contracts, and account activity. -For more information on how to use the API or integrate with Stellar/Soroban, explore our comprehensive [API documentation](https://docs.withobsrvr.com) or visit the Obsrvr Console at [console.withobsrvr.com](https://console.withobsrvr.com). +Obsrvr Lake sits above those layers. It keeps the raw record, then adds Stellar-native silver tables and gold metrics so teams can ask questions without rebuilding the indexer first. diff --git a/docs/lake/api/overview.md b/docs/lake/api/overview.md new file mode 100644 index 0000000..97e82a8 --- /dev/null +++ b/docs/lake/api/overview.md @@ -0,0 +1,156 @@ +--- +sidebar_position: 1 +title: API Overview +displayed_sidebar: lake +--- + +# Lake API overview + +Lake exposes Stellar bronze, silver, semantic, and gold data through REST endpoints. Use silver endpoints for most application and analyst workflows. + +## Base URL + +```bash +export API_KEY="your-api-key" +export BASE="https://gateway.withobsrvr.com/lake/v1/testnet" +``` + +Use `/mainnet` instead of `/testnet` for mainnet access. + +## Authentication + +```bash +curl -H "Authorization: Api-Key $API_KEY" \ + "$BASE/api/v1/silver/accounts/current?account_id=GAIH3ULLFQ4DGSECF2AR555KZ4KNDGEKN4AFI4SU2M7B43MGK3QJZNSR" +``` + +## Response shape + +List endpoints return data plus pagination or metadata fields when applicable. + +```json +{ + "data": [], + "count": 10, + "cursor": "ODI5MDQ1OjE6ZGVzYw==", + "has_more": true, + "_meta": { + "scanned_ledger": 829045, + "available_ledgers": { + "oldest": 277, + "latest": 829045 + } + } +} +``` + +## Silver endpoints + +### Accounts + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/api/v1/silver/accounts` | List and search accounts | +| GET | `/api/v1/silver/accounts/current` | Current account state | +| GET | `/api/v1/silver/accounts/history` | Historical account snapshots | +| GET | `/api/v1/silver/accounts/top` | Top accounts by XLM balance | +| GET | `/api/v1/silver/accounts/{id}/balances` | XLM and trustline balances | +| GET | `/api/v1/silver/accounts/{id}/activity` | Account activity feed | + +### Transfers and operations + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/api/v1/silver/transfers` | Unified token transfers across classic and SAC activity | +| GET | `/api/v1/silver/payments` | Payment operations | +| GET | `/api/v1/silver/operations/enriched` | Operations with transaction context | +| GET | `/api/v1/silver/operations/soroban` | Soroban operations only | + +### Assets and tokens + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/api/v1/silver/assets` | List tracked assets | +| GET | `/api/v1/silver/assets/{code}:{issuer}/holders` | Asset holders | +| GET | `/api/v1/silver/assets/{code}:{issuer}/stats` | Asset statistics | +| GET | `/api/v1/silver/tokens/{contract_id}` | SEP-41 token metadata | +| GET | `/api/v1/silver/tokens/{contract_id}/balances` | Token holder balances | +| GET | `/api/v1/silver/tokens/{contract_id}/transfers` | Token transfer history | + +### Soroban contracts and events + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/api/v1/silver/contracts/top` | Most active contracts | +| GET | `/api/v1/silver/contracts/{id}/analytics` | Contract-level analytics | +| GET | `/api/v1/silver/contracts/{id}/interface` | Detected contract interface | +| GET | `/api/v1/silver/events` | CAP-67 unified event stream | +| GET | `/api/v1/silver/events/generic` | Raw contract events with topic filters | +| GET | `/api/v1/silver/events/by-contract` | Events for one contract | + +### Transactions and search + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/api/v1/silver/tx/{hash}/decoded` | Human-readable transaction summary | +| GET | `/api/v1/silver/tx/{hash}/diffs` | Balance and state diffs | +| GET | `/api/v1/silver/tx/{hash}/full` | Full transaction analysis | +| GET | `/api/v1/silver/search` | Search accounts, contracts, transactions, ledgers, and assets | + +## Semantic endpoints + +Semantic endpoints answer higher-level questions without requiring the caller to know every Stellar table. + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/api/v1/semantic/activities` | Unified on-chain activity feed | +| GET | `/api/v1/semantic/contracts` | Contract registry with type classification | +| GET | `/api/v1/semantic/contracts/functions` | Function-level contract stats | +| GET | `/api/v1/semantic/accounts/summary` | Account activity summary | +| GET | `/api/v1/semantic/flows` | Normalized value flows | + +## Gold endpoints + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/api/v1/gold/compliance/balances` | Point-in-time balances | +| GET | `/api/v1/gold/compliance/supply` | Supply timeline with audit trail | +| GET | `/api/v1/gold/compliance/archives` | Compliance archive index | +| GET | `/api/v1/gold/compliance/lineage` | Audit lineage and checksums | + +## Pagination + +List endpoints use cursor pagination. + +```bash +curl -H "Authorization: Api-Key $API_KEY" \ + "$BASE/api/v1/silver/transfers?asset_code=USDC&limit=100" + +curl -H "Authorization: Api-Key $API_KEY" \ + "$BASE/api/v1/silver/transfers?asset_code=USDC&limit=100&cursor=ODI5MDQ1OjE6ZGVzYw==" +``` + +Treat cursors as opaque strings. Do not parse or construct them. + +## Errors + +```json +{ + "error": "invalid account_id" +} +``` + +| Status code | Meaning | +|-------------|---------| +| 200 | Success | +| 400 | Invalid parameter | +| 401 | Missing or invalid API key | +| 404 | Resource not found | +| 429 | Rate limit exceeded | +| 500 | Server error | + +## Next steps + +- [Run the Lake quickstart](/docs/lake/getting-started/quickstart) +- [Copy query examples](/docs/lake/guides/query-examples) +- [Understand Lake architecture](/docs/lake/architecture/overview) diff --git a/docs/lake/architecture/overview.md b/docs/lake/architecture/overview.md new file mode 100644 index 0000000..226f89e --- /dev/null +++ b/docs/lake/architecture/overview.md @@ -0,0 +1,101 @@ +--- +sidebar_position: 1 +title: Architecture Overview +displayed_sidebar: lake +--- + +# Architecture Overview + +OBSRVR Lake implements a lambda architecture optimized for blockchain data, combining real-time streaming with batch processing to provide both low-latency queries and complete historical access. + +## System Components + +Lake consists of 8 interconnected microservices: + +### Ingestion Layer + +**stellar-postgres-ingester** +- Receives real-time ledger data via gRPC +- Writes to PostgreSQL hot buffer using UNLOGGED tables +- Optimized for high-throughput writes + +### Transformation Layer + +**silver-realtime-transformer** +- Polls PostgreSQL hot buffer +- Enriches and transforms raw data +- Writes to PostgreSQL silver_hot tables + +**contract-event-index-transformer** +- Indexes contract events by ledger +- Enables O(1) lookups for contract queries + +**index-plane-transformer** +- Creates transaction hash index +- Enables fast (~500ms) hash lookups at scale + +### Cold Storage Archival + +**postgres-ducklake-flusher** +- Moves data from hot buffer to cold storage +- Uses high-watermark pattern for safe, idempotent transfers +- Writes Parquet files to S3/B2 + +**silver-cold-flusher** +- Archives silver layer data to cold storage +- Maintains analytics-ready historical data + +### Query Layer + +**stellar-query-api** +- REST API for all queries +- Transparently routes to hot or cold storage +- Merges results when queries span both layers + +## Data Flow + +``` +Stellar Network + ↓ +stellar-live-source-datalake (gRPC stream) + ↓ +stellar-postgres-ingester + ↓ +PostgreSQL Hot Buffer (UNLOGGED tables) + ↓ + ├──→ silver-realtime-transformer → PostgreSQL Silver Hot + │ + ├──→ postgres-ducklake-flusher → DuckLake Bronze (S3/B2) + │ + └──→ silver-cold-flusher → DuckLake Silver (S3/B2) + +stellar-query-api ←── queries both hot and cold storage +``` + +## High-Watermark Flush Pattern + +Data moves from hot to cold storage using a safe, idempotent pattern: + +1. **MARK**: Record the maximum ledger_sequence in hot storage +2. **FLUSH**: Copy all data ≤ watermark to cold storage +3. **DELETE**: Remove flushed data from hot storage +4. **VACUUM**: Periodically reclaim space + +This ensures no data loss even if the process is interrupted. + +## Storage Characteristics + +### Hot Storage (PostgreSQL) + +- **Tables**: UNLOGGED for maximum write performance +- **Retention**: ~10-20 minutes of recent data +- **Query Latency**: ~100ms +- **Purpose**: Real-time queries on recent data + +### Cold Storage (DuckLake) + +- **Format**: Parquet files on S3/B2 +- **Partitioning**: By ledger_range for efficient pruning +- **Compression**: Snappy +- **Query Latency**: ~2 seconds +- **Purpose**: Historical queries and analytics diff --git a/docs/lake/getting-started/quickstart.md b/docs/lake/getting-started/quickstart.md new file mode 100644 index 0000000..40784ba --- /dev/null +++ b/docs/lake/getting-started/quickstart.md @@ -0,0 +1,91 @@ +--- +sidebar_position: 1 +title: Quick Start +displayed_sidebar: lake +--- + +# Lake quickstart + +Run three Lake queries in five minutes: account balances, token transfers, and Soroban contract activity. + +## Prerequisites + +- An Obsrvr API key from [Console](https://console.withobsrvr.com) +- `curl` + +Set your environment: + +```bash +export API_KEY="your-api-key" +export BASE="https://gateway.withobsrvr.com/lake/v1/testnet" +``` + +All examples use testnet. Switch to mainnet by replacing `/testnet` with `/mainnet` when your key has mainnet access. + +## 1. Get an account's balances + +This returns native XLM plus trustline and token balances for a Stellar account. + +```bash +curl -H "Authorization: Api-Key $API_KEY" \ + "$BASE/api/v1/silver/accounts/GAIH3ULLFQ4DGSECF2AR555KZ4KNDGEKN4AFI4SU2M7B43MGK3QJZNSR/balances" +``` + +Use this for wallet portfolio views, account screens, and analyst lookups. + +## 2. Query USDC transfers + +Lake's silver transfer endpoint normalizes value movement so you do not have to join operations, effects, and SAC events yourself. + +```bash +curl -H "Authorization: Api-Key $API_KEY" \ + "$BASE/api/v1/silver/transfers?asset_code=USDC&limit=10" +``` + +Add an account filter when you need activity for one user: + +```bash +curl -H "Authorization: Api-Key $API_KEY" \ + "$BASE/api/v1/silver/transfers?asset_code=USDC&from_account=GAIH3ULLFQ4DGSECF2AR555KZ4KNDGEKN4AFI4SU2M7B43MGK3QJZNSR&limit=10" +``` + +## 3. Find active Soroban contracts + +This returns contracts ranked by recent activity. + +```bash +curl -H "Authorization: Api-Key $API_KEY" \ + "$BASE/api/v1/silver/contracts/top?period=24h&limit=10" +``` + +Use it for explorers, protocol monitoring, and contract discovery. + +## 4. Read contract events by topic + +If you know the event topic, query it directly. This example finds transfer events. + +```bash +curl -H "Authorization: Api-Key $API_KEY" \ + "$BASE/api/v1/silver/events/generic?topic0=transfer&limit=10" +``` + +This is the Lake version of starting from `getEvents`, but with access to the rest of the silver model when you need balances, transfers, accounts, and decoded transactions. + +## 5. Get a decoded transaction + +```bash +curl -H "Authorization: Api-Key $API_KEY" \ + "$BASE/api/v1/silver/tx/YOUR_TX_HASH/decoded" +``` + +Use the decoded transaction endpoint when you need a human-readable summary, balance diffs, or transaction context without parsing XDR. + +## What to use next + +| If you need | Read next | +|-------------|-----------| +| More examples | [Query Examples](/docs/lake/guides/query-examples) | +| API parameters | [Lake API Reference](/docs/lake/api/overview) | +| Medallion architecture | [Lake Overview](/docs/lake/overview) | +| Custom pipelines | [Flow Overview](/docs/flow/overview) | +| Horizon or Stellar RPC | [Gateway Overview](/docs/gateway/overview) | diff --git a/docs/lake/guides/query-examples.md b/docs/lake/guides/query-examples.md new file mode 100644 index 0000000..c309ad7 --- /dev/null +++ b/docs/lake/guides/query-examples.md @@ -0,0 +1,119 @@ +--- +sidebar_position: 1 +title: Query Examples +displayed_sidebar: lake +--- + +# Lake query examples + +Copy-paste queries for common Stellar analysis tasks. + +```bash +export API_KEY="your-api-key" +export BASE="https://gateway.withobsrvr.com/lake/v1/testnet" +``` + +## Account balances + +```bash +curl -H "Authorization: Api-Key $API_KEY" \ + "$BASE/api/v1/silver/accounts/GAIH3ULLFQ4DGSECF2AR555KZ4KNDGEKN4AFI4SU2M7B43MGK3QJZNSR/balances" +``` + +Use this for wallet portfolio views and account investigations. + +## Current account state + +```bash +curl -H "Authorization: Api-Key $API_KEY" \ + "$BASE/api/v1/silver/accounts/current?account_id=GAIH3ULLFQ4DGSECF2AR555KZ4KNDGEKN4AFI4SU2M7B43MGK3QJZNSR" +``` + +Returns sequence number, XLM balance, subentry count, and last modified ledger. + +## Token transfers by asset + +```bash +curl -H "Authorization: Api-Key $API_KEY" \ + "$BASE/api/v1/silver/transfers?asset_code=USDC&limit=20" +``` + +Lake normalizes classic payments and Soroban/SAC transfers into one transfer model. + +## Token transfers by account + +```bash +curl -H "Authorization: Api-Key $API_KEY" \ + "$BASE/api/v1/silver/transfers?from_account=GAIH3ULLFQ4DGSECF2AR555KZ4KNDGEKN4AFI4SU2M7B43MGK3QJZNSR&asset_code=USDC&limit=20" +``` + +Use `to_account` instead of `from_account` for incoming transfers. + +## Top holders for an issued asset + +```bash +curl -H "Authorization: Api-Key $API_KEY" \ + "$BASE/api/v1/silver/assets/USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN/holders?limit=10" +``` + +Use this for supply concentration, holder reports, and token dashboards. + +## Top Soroban contracts + +```bash +curl -H "Authorization: Api-Key $API_KEY" \ + "$BASE/api/v1/silver/contracts/top?period=24h&limit=10" +``` + +Returns the most active contracts by recent activity. + +## Contract analytics + +```bash +curl -H "Authorization: Api-Key $API_KEY" \ + "$BASE/api/v1/silver/contracts/CAUGJT4GREIY3WHOUUU5RIUDGSPVREF5CDCYJOWMHOVT2GWQT5JEETGJ/analytics" +``` + +Use this when you need callers, callees, functions, and activity over time for one contract. + +## Contract events by topic + +```bash +curl -H "Authorization: Api-Key $API_KEY" \ + "$BASE/api/v1/silver/events/generic?topic0=transfer&limit=10" +``` + +This is useful when you know the CAP-67 or application topic you want. + +## Transaction summary + +```bash +curl -H "Authorization: Api-Key $API_KEY" \ + "$BASE/api/v1/silver/tx/YOUR_TX_HASH/decoded" +``` + +Use decoded transactions when you need a human-readable summary instead of raw XDR. + +## Search + +```bash +curl -H "Authorization: Api-Key $API_KEY" \ + "$BASE/api/v1/silver/search?q=USDC" +``` + +Search spans accounts, contracts, transactions, ledgers, and assets. + +## Point-in-time compliance balances + +```bash +curl -H "Authorization: Api-Key $API_KEY" \ + "$BASE/api/v1/gold/compliance/balances?asset_code=XLM×tamp=2026-01-12T23:59:59Z&limit=100" +``` + +Use gold compliance endpoints for defensible reporting with lineage. + +## Why not just use `getEvents`? + +Use `getEvents` when you want raw Soroban events and already know the exact contract, topic filters, and cursor model. + +Use Lake when you need the event plus context: accounts, token balances, transfer normalization, decoded transactions, contract analytics, and historical joins across Stellar classic and Soroban activity. diff --git a/docs/lake/overview.md b/docs/lake/overview.md new file mode 100644 index 0000000..3c8e353 --- /dev/null +++ b/docs/lake/overview.md @@ -0,0 +1,90 @@ +--- +sidebar_position: 1 +title: Overview +displayed_sidebar: lake +--- + +# Obsrvr Lake + +Obsrvr Lake is a medallion-architecture data warehouse for Stellar. It stores decoded ledger data and exposes analytics-ready tables for token transfers, accounts, Soroban contracts, contract events, fees, and network metrics. + +Use Lake when you want the answer without building the indexer first. + +```bash +export API_KEY="your-api-key" +export BASE="https://gateway.withobsrvr.com/lake/v1/testnet" + +curl -H "Authorization: Api-Key $API_KEY" \ + "$BASE/api/v1/silver/accounts/GAIH3ULLFQ4DGSECF2AR555KZ4KNDGEKN4AFI4SU2M7B43MGK3QJZNSR/balances" +``` + +## The medallion model + +```text +Stellar ledger stream + │ + ▼ +Bronze Raw decoded ledgers, transactions, operations, effects, trades, contract events + │ + ▼ +Silver Normalized analytics tables for transfers, accounts, contracts, prices, events + │ + ▼ +Gold Business-ready metrics for compliance, stablecoin volume, fees, and network health +``` + +| Layer | What it contains | Who uses it | +|-------|------------------|-------------| +| Bronze | Raw decoded Stellar data with full lineage | Teams that need auditability or custom derivations | +| Silver | Pre-joined, normalized tables and API endpoints | Wallets, explorers, analysts, dashboards, protocols | +| Gold | Curated business metrics and compliance outputs | Analysts, compliance teams, reporting workflows | + +## Why Lake exists + +Stellar data is not hard because ledgers are unavailable. It is hard because the useful answers are spread across transaction envelopes, operation results, effects, trustlines, contract events, SAC events, and Soroban diagnostic data. + +Lake turns those pieces into tables that match the questions teams ask: + +- token transfers by account, asset, issuer, or contract +- account balances and historical snapshots +- Soroban contract calls and call graphs +- CAP-67 events by contract or topic +- top contracts, holders, assets, and trading pairs +- fee distributions and network health metrics +- compliance archives and point-in-time balances + +## Silver tables to start with + +| Domain | Useful tables / endpoints | Typical question | +|--------|---------------------------|------------------| +| Accounts | `/silver/accounts/current`, `/silver/accounts/{id}/balances`, `/silver/accounts/{id}/activity` | What does this account hold and what has it done? | +| Transfers | `/silver/transfers`, `/silver/tokens/{contract_id}/transfers` | What value moved between accounts? | +| Contracts | `/silver/contracts/top`, `/silver/contracts/{id}/analytics` | Which Soroban contracts are active and who calls them? | +| Events | `/silver/events`, `/silver/events/generic`, `/silver/events/by-contract` | What CAP-67 or raw events did this contract emit? | +| Transactions | `/silver/tx/{hash}/decoded`, `/silver/tx/{hash}/full` | What happened in this transaction? | +| Prices | `/silver/prices/pairs`, `/silver/prices/{base}/{counter}/latest` | What pairs trade and at what price? | +| Gold compliance | `/gold/compliance/balances`, `/gold/compliance/lineage` | What balances existed at a point in time, with audit lineage? | + +## Hot and cold storage + +Lake uses hot storage for recent data and cold storage for full history. The API chooses the right layer and merges results. + +| Storage | Data range | Typical use | +|---------|------------|-------------| +| Hot | Recent ledgers | dashboards, fresh account state, new contract activity | +| Cold | Full history in Parquet/DuckLake | historical scans, compliance reports, backfills | + +You do not choose hot or cold in the request. You query Lake once. + +## Compared to `getEvents` + +`getEvents` is useful when you already know the contract, topics, cursor, and event shape you want. It returns contract events. + +Lake is different. Lake keeps events, but also normalizes them into Stellar-native tables: transfers, token balances, account activity, contract analytics, decoded transactions, and compliance outputs. If you want to know who received USDC, which SAC transfer affected an account, or what a transaction did across classic and Soroban operations, Lake saves you the parsing layer. + +## Next steps + +- [Run your first Lake query](/docs/lake/getting-started/quickstart) +- [Copy common query examples](/docs/lake/guides/query-examples) +- [Review the Lake API reference](/docs/lake/api/overview) +- [See the architecture](/docs/lake/architecture/overview) diff --git a/docs/nodes/getting-started/quickstart.md b/docs/nodes/getting-started/quickstart.md new file mode 100644 index 0000000..84ed0c6 --- /dev/null +++ b/docs/nodes/getting-started/quickstart.md @@ -0,0 +1,53 @@ +--- +sidebar_position: 1 +title: Quick Start +displayed_sidebar: nodes +--- + +# Quick Start + +Get started with OBSRVR Nodes to deploy your dedicated Stellar infrastructure. + +## Prerequisites + +- An OBSRVR account ([sign up](https://console.withobsrvr.com)) +- A project that requires dedicated Stellar infrastructure + +## Provisioning a Node + +1. Log in to the [OBSRVR Console](https://console.withobsrvr.com) +2. Navigate to **Nodes** in the sidebar +3. Click **Create Node** +4. Select your node type: + - **Horizon** - Full Stellar Horizon API + - **RPC** - Stellar RPC for Soroban smart contracts +5. Configure your node settings +6. Review pricing and confirm + +Your node will be provisioned within minutes. + +## Connecting to Your Node + +Once provisioned, you'll receive dedicated endpoints for your node: + +### Horizon Node + +```bash +# Your dedicated Horizon endpoint +curl "https://your-node-id.nodes.withobsrvr.com/ledgers?limit=10" +``` + +### RPC Node + +```bash +# Your dedicated RPC endpoint +curl -X POST "https://your-node-id.rpc.withobsrvr.com" \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","id":1,"method":"getHealth"}' +``` + +## Next Steps + +- Configure [Asset Whitelisting](/docs/nodes/guides/asset-whitelisting) to customize data retention +- Review [Pricing](/docs/nodes/overview#pricing) details +- Contact support for enterprise requirements diff --git a/docs/nodes/guides/asset-whitelisting.md b/docs/nodes/guides/asset-whitelisting.md new file mode 100644 index 0000000..13f4cdc --- /dev/null +++ b/docs/nodes/guides/asset-whitelisting.md @@ -0,0 +1,48 @@ +--- +sidebar_position: 1 +title: Asset Whitelisting +displayed_sidebar: nodes +--- + +# Asset Whitelisting + +Asset whitelisting lets you control which Stellar assets your dedicated OBSRVR Node retains in history. This helps reduce storage usage and keeps your node focused on the data your application needs. + +## When to Use Asset Whitelisting + +Use asset whitelisting when you want to: + +- Retain history only for specific assets +- Reduce storage and indexing costs +- Improve query performance for targeted workloads +- Support compliance or reporting requirements for a known asset set + +## Configure a Whitelist + +1. Open the [OBSRVR Console](https://console.withobsrvr.com) +2. Navigate to **Nodes** +3. Select your node +4. Open **Data Retention** or **Asset Whitelisting** +5. Add the assets you want to retain +6. Save the configuration + +## Asset Identifiers + +For Stellar assets, provide: + +- Asset code, such as `USDC` +- Asset issuer account ID + +Native XLM does not require an issuer. + +## Example + +```text +Asset code: USDC +Issuer: GA5ZSEJYB37EA2QJ...EXAMPLE +Network: mainnet +``` + +## Notes + +Changes may take time to apply depending on the current node state and the amount of retained history. Contact support if you need help planning a retention policy for production workloads. diff --git a/docs/nodes/overview.md b/docs/nodes/overview.md new file mode 100644 index 0000000..d0df02a --- /dev/null +++ b/docs/nodes/overview.md @@ -0,0 +1,65 @@ +--- +sidebar_position: 1 +title: Overview +displayed_sidebar: nodes +--- + +# OBSRVR Nodes + +OBSRVR Nodes provides dedicated Stellar infrastructure for your applications. Each node operates as an isolated instance with resources exclusively available to your workloads, ensuring enhanced security and consistent performance. + +## Key Features + +### Dedicated Instances + +Each OBSRVR Node operates as a dedicated instance, ensuring that resources are exclusively available to your applications. No noisy neighbors, no shared resources. + +### Asset Whitelisting + +Customize which assets your node will retain in history through an easy-to-use asset whitelisting feature. Keep only the data you need, reducing storage costs and improving query performance. + +### 24/7 Technical Support + +Round-the-clock assistance is available for node operation and management issues. + +## Available Infrastructure + +### Stellar Horizon + +Full Stellar Horizon API access with dedicated resources: +- Complete transaction history +- Account and ledger queries +- Transaction submission +- Customizable history retention + +### Stellar RPC (Soroban) + +Dedicated Stellar RPC endpoints for smart contract interactions: +- Contract invocation +- State queries +- Event streaming +- Simulation endpoints + +## Pricing + +OBSRVR Nodes uses **per-hour usage-based pricing**. You pay only for the compute resources consumed while your nodes are active, allowing cost-effective scaling as your application grows. + +- No long-term commitments +- Cancel anytime without penalties +- Scale up or down based on demand + +## Use Cases + +- **High-traffic applications** requiring dedicated resources +- **Enterprise deployments** needing isolated infrastructure +- **Custom data retention** with asset whitelisting +- **Compliance requirements** requiring dedicated environments + +## Getting Started + +Visit the [OBSRVR Console](https://console.withobsrvr.com) to provision your first dedicated node, or contact us for enterprise requirements. + +## Next Steps + +- [Quick Start](/docs/nodes/getting-started/quickstart) - Provision your first node +- [Asset Whitelisting](/docs/nodes/guides/asset-whitelisting) - Configure data retention diff --git a/docs/products/index.md b/docs/products/index.md deleted file mode 100644 index b89de0f..0000000 --- a/docs/products/index.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -description: Create a doc page with rich content. ---- - -# Hello from Docusaurus - -Are you ready to create the documentation site for your open source project? - -## Headers - -will show up on the table of contents on the upper right - -So that your users will know what this page is all about without scrolling down or even without reading too much. - -## Only h2 and h3 will be in the TOC by default. - -You can configure the TOC heading levels either per-document or in the theme configuration. - -The headers are well-spaced so that the hierarchy is clear. - -- lists will help you -- present the key points -- that you want your users to remember - - and you may nest them - - multiple times \ No newline at end of file diff --git a/docs/tutorial-basics/_category_.json b/docs/tutorial-basics/_category_.json deleted file mode 100644 index 2e6db55..0000000 --- a/docs/tutorial-basics/_category_.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "label": "Tutorial - Basics", - "position": 2, - "link": { - "type": "generated-index", - "description": "5 minutes to learn the most important Docusaurus concepts." - } -} diff --git a/docs/tutorial-basics/congratulations.md b/docs/tutorial-basics/congratulations.md deleted file mode 100644 index 04771a0..0000000 --- a/docs/tutorial-basics/congratulations.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -sidebar_position: 6 ---- - -# Congratulations! - -You have just learned the **basics of Docusaurus** and made some changes to the **initial template**. - -Docusaurus has **much more to offer**! - -Have **5 more minutes**? Take a look at **[versioning](../tutorial-extras/manage-docs-versions.md)** and **[i18n](../tutorial-extras/translate-your-site.md)**. - -Anything **unclear** or **buggy** in this tutorial? [Please report it!](https://github.com/facebook/docusaurus/discussions/4610) - -## What's next? - -- Read the [official documentation](https://docusaurus.io/) -- Modify your site configuration with [`docusaurus.config.js`](https://docusaurus.io/docs/api/docusaurus-config) -- Add navbar and footer items with [`themeConfig`](https://docusaurus.io/docs/api/themes/configuration) -- Add a custom [Design and Layout](https://docusaurus.io/docs/styling-layout) -- Add a [search bar](https://docusaurus.io/docs/search) -- Find inspirations in the [Docusaurus showcase](https://docusaurus.io/showcase) -- Get involved in the [Docusaurus Community](https://docusaurus.io/community/support) diff --git a/docs/tutorial-basics/create-a-blog-post.md b/docs/tutorial-basics/create-a-blog-post.md deleted file mode 100644 index 550ae17..0000000 --- a/docs/tutorial-basics/create-a-blog-post.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -sidebar_position: 3 ---- - -# Create a Blog Post - -Docusaurus creates a **page for each blog post**, but also a **blog index page**, a **tag system**, an **RSS** feed... - -## Create your first Post - -Create a file at `blog/2021-02-28-greetings.md`: - -```md title="blog/2021-02-28-greetings.md" ---- -slug: greetings -title: Greetings! -authors: - - name: Joel Marcey - title: Co-creator of Docusaurus 1 - url: https://github.com/JoelMarcey - image_url: https://github.com/JoelMarcey.png - - name: Sébastien Lorber - title: Docusaurus maintainer - url: https://sebastienlorber.com - image_url: https://github.com/slorber.png -tags: [greetings] ---- - -Congratulations, you have made your first post! - -Feel free to play around and edit this post as much as you like. -``` - -A new blog post is now available at [http://localhost:3000/blog/greetings](http://localhost:3000/blog/greetings). diff --git a/docs/tutorial-basics/create-a-document.md b/docs/tutorial-basics/create-a-document.md deleted file mode 100644 index c22fe29..0000000 --- a/docs/tutorial-basics/create-a-document.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -sidebar_position: 2 ---- - -# Create a Document - -Documents are **groups of pages** connected through: - -- a **sidebar** -- **previous/next navigation** -- **versioning** - -## Create your first Doc - -Create a Markdown file at `docs/hello.md`: - -```md title="docs/hello.md" -# Hello - -This is my **first Docusaurus document**! -``` - -A new document is now available at [http://localhost:3000/docs/hello](http://localhost:3000/docs/hello). - -## Configure the Sidebar - -Docusaurus automatically **creates a sidebar** from the `docs` folder. - -Add metadata to customize the sidebar label and position: - -```md title="docs/hello.md" {1-4} ---- -sidebar_label: 'Hi!' -sidebar_position: 3 ---- - -# Hello - -This is my **first Docusaurus document**! -``` - -It is also possible to create your sidebar explicitly in `sidebars.js`: - -```js title="sidebars.js" -export default { - tutorialSidebar: [ - 'intro', - // highlight-next-line - 'hello', - { - type: 'category', - label: 'Tutorial', - items: ['tutorial-basics/create-a-document'], - }, - ], -}; -``` diff --git a/docs/tutorial-basics/create-a-page.md b/docs/tutorial-basics/create-a-page.md deleted file mode 100644 index 20e2ac3..0000000 --- a/docs/tutorial-basics/create-a-page.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -sidebar_position: 1 ---- - -# Create a Page - -Add **Markdown or React** files to `src/pages` to create a **standalone page**: - -- `src/pages/index.js` → `localhost:3000/` -- `src/pages/foo.md` → `localhost:3000/foo` -- `src/pages/foo/bar.js` → `localhost:3000/foo/bar` - -## Create your first React Page - -Create a file at `src/pages/my-react-page.js`: - -```jsx title="src/pages/my-react-page.js" -import React from 'react'; -import Layout from '@theme/Layout'; - -export default function MyReactPage() { - return ( - -

My React page

-

This is a React page

-
- ); -} -``` - -A new page is now available at [http://localhost:3000/my-react-page](http://localhost:3000/my-react-page). - -## Create your first Markdown Page - -Create a file at `src/pages/my-markdown-page.md`: - -```mdx title="src/pages/my-markdown-page.md" -# My Markdown page - -This is a Markdown page -``` - -A new page is now available at [http://localhost:3000/my-markdown-page](http://localhost:3000/my-markdown-page). diff --git a/docs/tutorial-basics/deploy-your-site.md b/docs/tutorial-basics/deploy-your-site.md deleted file mode 100644 index 1c50ee0..0000000 --- a/docs/tutorial-basics/deploy-your-site.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -sidebar_position: 5 ---- - -# Deploy your site - -Docusaurus is a **static-site-generator** (also called **[Jamstack](https://jamstack.org/)**). - -It builds your site as simple **static HTML, JavaScript and CSS files**. - -## Build your site - -Build your site **for production**: - -```bash -npm run build -``` - -The static files are generated in the `build` folder. - -## Deploy your site - -Test your production build locally: - -```bash -npm run serve -``` - -The `build` folder is now served at [http://localhost:3000/](http://localhost:3000/). - -You can now deploy the `build` folder **almost anywhere** easily, **for free** or very small cost (read the **[Deployment Guide](https://docusaurus.io/docs/deployment)**). diff --git a/docs/tutorial-basics/markdown-features.mdx b/docs/tutorial-basics/markdown-features.mdx deleted file mode 100644 index 35e0082..0000000 --- a/docs/tutorial-basics/markdown-features.mdx +++ /dev/null @@ -1,152 +0,0 @@ ---- -sidebar_position: 4 ---- - -# Markdown Features - -Docusaurus supports **[Markdown](https://daringfireball.net/projects/markdown/syntax)** and a few **additional features**. - -## Front Matter - -Markdown documents have metadata at the top called [Front Matter](https://jekyllrb.com/docs/front-matter/): - -```text title="my-doc.md" -// highlight-start ---- -id: my-doc-id -title: My document title -description: My document description -slug: /my-custom-url ---- -// highlight-end - -## Markdown heading - -Markdown text with [links](./hello.md) -``` - -## Links - -Regular Markdown links are supported, using url paths or relative file paths. - -```md -Let's see how to [Create a page](/create-a-page). -``` - -```md -Let's see how to [Create a page](./create-a-page.md). -``` - -**Result:** Let's see how to [Create a page](./create-a-page.md). - -## Images - -Regular Markdown images are supported. - -You can use absolute paths to reference images in the static directory (`static/img/docusaurus.png`): - -```md -![Docusaurus logo](/img/docusaurus.png) -``` - -![Docusaurus logo](/img/docusaurus.png) - -You can reference images relative to the current file as well. This is particularly useful to colocate images close to the Markdown files using them: - -```md -![Docusaurus logo](./img/docusaurus.png) -``` - -## Code Blocks - -Markdown code blocks are supported with Syntax highlighting. - -````md -```jsx title="src/components/HelloDocusaurus.js" -function HelloDocusaurus() { - return

Hello, Docusaurus!

; -} -``` -```` - -```jsx title="src/components/HelloDocusaurus.js" -function HelloDocusaurus() { - return

Hello, Docusaurus!

; -} -``` - -## Admonitions - -Docusaurus has a special syntax to create admonitions and callouts: - -```md -:::tip My tip - -Use this awesome feature option - -::: - -:::danger Take care - -This action is dangerous - -::: -``` - -:::tip My tip - -Use this awesome feature option - -::: - -:::danger Take care - -This action is dangerous - -::: - -## MDX and React Components - -[MDX](https://mdxjs.com/) can make your documentation more **interactive** and allows using any **React components inside Markdown**: - -```jsx -export const Highlight = ({children, color}) => ( - { - alert(`You clicked the color ${color} with label ${children}`) - }}> - {children} - -); - -This is Docusaurus green ! - -This is Facebook blue ! -``` - -export const Highlight = ({children, color}) => ( - { - alert(`You clicked the color ${color} with label ${children}`); - }}> - {children} - -); - -This is Docusaurus green ! - -This is Facebook blue ! diff --git a/docs/tutorial-extras/_category_.json b/docs/tutorial-extras/_category_.json deleted file mode 100644 index a8ffcc1..0000000 --- a/docs/tutorial-extras/_category_.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "label": "Tutorial - Extras", - "position": 3, - "link": { - "type": "generated-index" - } -} diff --git a/docs/tutorial-extras/img/docsVersionDropdown.png b/docs/tutorial-extras/img/docsVersionDropdown.png deleted file mode 100644 index 97e4164..0000000 Binary files a/docs/tutorial-extras/img/docsVersionDropdown.png and /dev/null differ diff --git a/docs/tutorial-extras/img/localeDropdown.png b/docs/tutorial-extras/img/localeDropdown.png deleted file mode 100644 index e257edc..0000000 Binary files a/docs/tutorial-extras/img/localeDropdown.png and /dev/null differ diff --git a/docs/tutorial-extras/manage-docs-versions.md b/docs/tutorial-extras/manage-docs-versions.md deleted file mode 100644 index ccda0b9..0000000 --- a/docs/tutorial-extras/manage-docs-versions.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -sidebar_position: 1 ---- - -# Manage Docs Versions - -Docusaurus can manage multiple versions of your docs. - -## Create a docs version - -Release a version 1.0 of your project: - -```bash -npm run docusaurus docs:version 1.0 -``` - -The `docs` folder is copied into `versioned_docs/version-1.0` and `versions.json` is created. - -Your docs now have 2 versions: - -- `1.0` at `http://localhost:3000/docs/` for the version 1.0 docs -- `current` at `http://localhost:3000/docs/next/` for the **upcoming, unreleased docs** - -## Add a Version Dropdown - -To navigate seamlessly across versions, add a version dropdown. - -Modify the `docusaurus.config.js` file: - -```js title="docusaurus.config.js" -export default { - themeConfig: { - navbar: { - items: [ - // highlight-start - { - type: 'docsVersionDropdown', - }, - // highlight-end - ], - }, - }, -}; -``` - -The docs version dropdown appears in your navbar: - -![Docs Version Dropdown](./img/docsVersionDropdown.png) - -## Update an existing version - -It is possible to edit versioned docs in their respective folder: - -- `versioned_docs/version-1.0/hello.md` updates `http://localhost:3000/docs/hello` -- `docs/hello.md` updates `http://localhost:3000/docs/next/hello` diff --git a/docs/tutorial-extras/translate-your-site.md b/docs/tutorial-extras/translate-your-site.md deleted file mode 100644 index b5a644a..0000000 --- a/docs/tutorial-extras/translate-your-site.md +++ /dev/null @@ -1,88 +0,0 @@ ---- -sidebar_position: 2 ---- - -# Translate your site - -Let's translate `docs/intro.md` to French. - -## Configure i18n - -Modify `docusaurus.config.js` to add support for the `fr` locale: - -```js title="docusaurus.config.js" -export default { - i18n: { - defaultLocale: 'en', - locales: ['en', 'fr'], - }, -}; -``` - -## Translate a doc - -Copy the `docs/intro.md` file to the `i18n/fr` folder: - -```bash -mkdir -p i18n/fr/docusaurus-plugin-content-docs/current/ - -cp docs/intro.md i18n/fr/docusaurus-plugin-content-docs/current/intro.md -``` - -Translate `i18n/fr/docusaurus-plugin-content-docs/current/intro.md` in French. - -## Start your localized site - -Start your site on the French locale: - -```bash -npm run start -- --locale fr -``` - -Your localized site is accessible at [http://localhost:3000/fr/](http://localhost:3000/fr/) and the `Getting Started` page is translated. - -:::caution - -In development, you can only use one locale at a time. - -::: - -## Add a Locale Dropdown - -To navigate seamlessly across languages, add a locale dropdown. - -Modify the `docusaurus.config.js` file: - -```js title="docusaurus.config.js" -export default { - themeConfig: { - navbar: { - items: [ - // highlight-start - { - type: 'localeDropdown', - }, - // highlight-end - ], - }, - }, -}; -``` - -The locale dropdown now appears in your navbar: - -![Locale Dropdown](./img/localeDropdown.png) - -## Build your localized site - -Build your site for a specific locale: - -```bash -npm run build -- --locale fr -``` - -Or build your site to include all the locales at once: - -```bash -npm run build -``` diff --git a/docusaurus.config.js b/docusaurus.config.js index 424da86..2342126 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -41,9 +41,8 @@ const config = { ({ docs: { sidebarPath: "./sidebars.js", - // Please change this to your repo. - // Remove this to remove the "edit this page" links. }, + blog: false, theme: { customCss: "./src/css/custom.css", }, @@ -54,11 +53,14 @@ const config = { themeConfig: /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ ({ - defaultMode: "dark", + colorMode: { + defaultMode: "light", + respectPrefersColorScheme: true, + }, // Replace with your project's social card image: "img/docusaurus-social-card.jpg", navbar: { - title: "Docs", + title: "obsrvr", logo: { alt: "Obsrvr Logo", src: "img/obsrvr_black.png", @@ -97,7 +99,7 @@ const config = { title: "Docs", items: [ { - label: "Tutorial", + label: "Getting Started", to: "/docs/intro", }, ], diff --git a/sidebars.js b/sidebars.js index dc8efbe..196d357 100644 --- a/sidebars.js +++ b/sidebars.js @@ -1,111 +1,151 @@ -/** - * Creating a sidebar enables you to: - - create an ordered group of docs - - render a sidebar for each doc of that group - - provide next/previous navigation - - The sidebars can be generated from the filesystem, or explicitly defined here. - - Create as many sidebars as you want. - */ - // @ts-check /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ const sidebars = { - // By default, Docusaurus generates a sidebar from the docs folder structure - tutorialSidebar: [{ type: "autogenerated", dirName: "." }], + // Main docs sidebar (for intro page) docs: [ 'intro', + ], + + // Gateway product sidebar + gateway: [ + { + type: 'doc', + id: 'gateway/overview', + label: 'Overview', + }, { type: 'category', - label: 'Gateway', + label: 'Guides', collapsed: false, items: [ - 'gateway/overview', - { - type: 'category', - label: 'Guides', - items: [ - 'gateway/guides/stellar-rpc-full-history', - ], - }, + 'gateway/guides/stellar-rpc-full-history', ], }, + ], + + // Flow product sidebar + flow: [ + { + type: 'doc', + id: 'flow/overview', + label: 'Overview', + }, { type: 'category', - label: 'Flow', + label: 'Getting Started', collapsed: false, items: [ - 'flow/overview', - { - type: 'category', - label: 'Getting Started', - items: [ - 'flow/getting-started/quickstart', - ], - }, - { - type: 'category', - label: 'Concepts', - items: [ - 'flow/concepts/pipelines', - ], - }, - { - type: 'category', - label: 'Processors', - items: [ - 'flow/processors/index', - 'flow/processors/payments-with-memo', - 'flow/processors/contract-events', - ], - }, - { - type: 'category', - label: 'Consumers', - items: [ - 'flow/consumers/index', - 'flow/consumers/postgresql', - ], - }, - 'flow/pricing', + 'flow/getting-started/quickstart', ], }, - ], - tutorial: [ { type: 'category', - label: 'Tutorial', - items: ['tutorial-basics/create-a-document'], + label: 'Concepts', + collapsed: false, + items: [ + 'flow/concepts/pipelines', + ], + }, + { + type: 'category', + label: 'API', + collapsed: false, + items: [ + 'flow/api', + ], + }, + { + type: 'category', + label: 'Processors', + collapsed: false, + items: [ + 'flow/processors/index', + 'flow/processors/payments-with-memo', + 'flow/processors/contract-events', + ], + }, + { + type: 'category', + label: 'Consumers', + collapsed: false, + items: [ + 'flow/consumers/index', + 'flow/consumers/postgresql', + ], + }, + { + type: 'doc', + id: 'flow/pricing', + label: 'Pricing', }, ], - products: [ + + // Lake product sidebar + lake: [ + { + type: 'doc', + id: 'lake/overview', + label: 'Overview', + }, + { + type: 'category', + label: 'Getting Started', + collapsed: false, + items: [ + 'lake/getting-started/quickstart', + ], + }, { type: 'category', - label: 'Products', - items: ['tutorial-extras/manage-docs-versions'], + label: 'Architecture', + collapsed: false, + items: [ + 'lake/architecture/overview', + ], + }, + { + type: 'category', + label: 'API Reference', + collapsed: false, + items: [ + 'lake/api/overview', + ], + }, + { + type: 'category', + label: 'Guides', + collapsed: false, + items: [ + 'lake/guides/query-examples', + ], }, ], - // But you can create a sidebar manually - /* - tutorialSidebar: [ - 'intro', - 'hello', + // Nodes product sidebar + nodes: [ + { + type: 'doc', + id: 'nodes/overview', + label: 'Overview', + }, + { + type: 'category', + label: 'Getting Started', + collapsed: false, + items: [ + 'nodes/getting-started/quickstart', + ], + }, { type: 'category', - label: 'Tutorial', - items: ['tutorial-basics/create-a-document'], + label: 'Guides', + collapsed: false, + items: [ + 'nodes/guides/asset-whitelisting', + ], }, ], - */ }; - - - -// export default { -// sidebars -// }; -module.exports = sidebars; \ No newline at end of file +module.exports = sidebars; diff --git a/src/components/ObsrvrTopNav/index.js b/src/components/ObsrvrTopNav/index.js new file mode 100644 index 0000000..0bf5c15 --- /dev/null +++ b/src/components/ObsrvrTopNav/index.js @@ -0,0 +1,161 @@ +import React, {useEffect, useMemo, useRef, useState} from 'react'; +import Link from '@docusaurus/Link'; +import {useLocation} from '@docusaurus/router'; +import {useColorMode} from '@docusaurus/theme-common'; + +function Ico({w = 16, children}) { + return {children}; +} + +const icons = { + search: (p) => , + sun: (p) => , + moon: (p) => , + arrowR: (p) => , + github: (p) => , + layers: (p) => , + zap: (p) => , + terminal: (p) => , + book: (p) => , + key: (p) => , +}; + +const searchIndex = [ + ['Obsrvr overview', '/docs/intro', 'Obsrvr product hierarchy Lake Flow Gateway Console decoded Stellar data'], + ['Obsrvr Lake', '/docs/lake/overview', 'bronze silver gold Stellar data warehouse token transfers accounts contracts metrics'], + ['Lake quickstart', '/docs/lake/getting-started/quickstart', 'query Lake account balances token transfers contract events decoded transaction'], + ['Lake query examples', '/docs/lake/guides/query-examples', 'USDC transfers holders contract analytics CAP-67 events search compliance balances'], + ['Lake API overview', '/docs/lake/api/overview', 'silver semantic gold endpoints accounts transfers contracts events transactions pagination'], + ['Lake architecture', '/docs/lake/architecture/overview', 'hot cold DuckLake PostgreSQL Parquet architecture'], + ['Flow overview', '/docs/flow/overview', 'custom Stellar data pipelines processors consumers managed runtime'], + ['Flow quickstart', '/docs/flow/getting-started/quickstart', 'create pipeline YAML ContractEvent PostgreSQL API apply validate'], + ['Flow Pipeline API', '/docs/flow/api', 'pipeline API apply validate start stop export registry secrets CI/CD'], + ['Flow pipeline concepts', '/docs/flow/concepts/pipelines', 'source processors consumers BufferedStorageSourceAdapter lifecycle billing'], + ['Flow processors', '/docs/flow/processors/', 'payments memo contract event account data raw transactions processor registry'], + ['Flow consumers', '/docs/flow/consumers/', 'PostgreSQL webhook Kafka S3 Redis consumer destination'], + ['Flow pricing', '/docs/flow/pricing', 'runtime metered billing pipeline minutes price'], + ['Gateway overview', '/docs/gateway/overview', 'Horizon Stellar RPC Gateway endpoints authentication Api-Key'], + ['Gateway full-history RPC', '/docs/gateway/guides/stellar-rpc-full-history', 'Stellar RPC full history guide'], + ['Nodes overview', '/docs/nodes/overview', 'dedicated Stellar infrastructure nodes Horizon RPC'], +]; + +function isActive(pathname, href) { + if (href === '/') return pathname === '/'; + if (href.startsWith('http')) return false; + return pathname === href || pathname.startsWith(`${href}/`); +} + +function SearchModal({open, onClose}) { + const [query, setQuery] = useState(''); + const inputRef = useRef(null); + const results = useMemo(() => { + const q = query.trim().toLowerCase(); + if (!q) return searchIndex.slice(0, 8); + const terms = q.split(/\s+/); + return searchIndex + .map(([title, href, text]) => { + const haystack = `${title} ${text}`.toLowerCase(); + const score = terms.reduce((sum, term) => sum + (haystack.includes(term) ? 1 : 0) + (title.toLowerCase().includes(term) ? 2 : 0), 0); + return {title, href, text, score}; + }) + .filter((item) => item.score > 0) + .sort((a, b) => b.score - a.score) + .slice(0, 10); + }, [query]); + + useEffect(() => { + if (open) { + setQuery(''); + setTimeout(() => inputRef.current?.focus(), 0); + } + }, [open]); + + if (!open) return null; + + return
{ if (e.target.classList.contains('cmdk-overlay')) onClose(); }}> +
+
+ {icons.search({})} + setQuery(e.target.value)} placeholder="Search docs…" aria-label="Search docs" /> + Esc +
+
+
+
{query ? 'Results' : 'Popular pages'}
+ {results.length ? results.map((item, index) => + {icons.book({})} + {item.title} + {item.href} + ) :
No results for “{query}”
} +
+
+
+ ⌘K Toggle + Esc Close +
+
+
; +} + +export default function ObsrvrTopNav() { + const {pathname} = useLocation(); + const {colorMode, setColorMode} = useColorMode(); + const [searchOpen, setSearchOpen] = useState(false); + const logoSrc = colorMode === 'dark' ? '/img/obsrvr_white.png' : '/img/obsrvr_black.png'; + const subnav = [ + ['Lake', icons.layers, '/docs/lake/overview'], + ['Query examples', icons.search, '/docs/lake/guides/query-examples'], + ['Flow', icons.zap, '/docs/flow/overview'], + ['Gateway', icons.terminal, '/docs/gateway/overview'], + ['Pricing', icons.book, '/docs/flow/pricing'], + ['Console', icons.key, 'https://console.withobsrvr.com'], + ]; + + useEffect(() => { + const onKey = (e) => { + if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') { + e.preventDefault(); + setSearchOpen((value) => !value); + } else if (e.key === 'Escape') { + setSearchOpen(false); + } + }; + const onOpenSearch = () => setSearchOpen(true); + window.addEventListener('keydown', onKey); + window.addEventListener('obsrvr:open-search', onOpenSearch); + return () => { + window.removeEventListener('keydown', onKey); + window.removeEventListener('obsrvr:open-search', onOpenSearch); + }; + }, []); + + return <> +
+ Obsrvr + Docs + +
+ + + {icons.github({})} + Sign in {icons.arrowR({w: 14})} +
+
+ {subnav.map(([label, Icon, href], index) => + {label} + )} +
+ setSearchOpen(false)} /> + ; +} diff --git a/src/css/custom.css b/src/css/custom.css index 81e668f..53311d1 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -1,83 +1,217 @@ -/** - * Any CSS included here will be global. The classic template - * bundles Infima by default. Infima is a CSS framework designed to - * work well for content-centric websites. - */ +/* OBSRVR Docusaurus theme adapted from obsrvr-screenshots/OBSRVR-Docs-readable.html */ +@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap'); -/* You can override the default Infima variables here. */ :root { - --ifm-color-primary: #667eea; - --ifm-color-primary-dark: #5a67d8; - --ifm-color-primary-darker: #4c51bf; - --ifm-color-primary-darkest: #434190; - --ifm-color-primary-light: #7c8ef8; - --ifm-color-primary-lighter: #8b9afe; - --ifm-color-primary-lightest: #a3b9ff; - --ifm-code-font-size: 95%; - --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); - - /* Enhanced typography */ - --ifm-font-family-base: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; + /* OBSRVR design tokens */ + --obs-bg: #fafaf8; + --obs-bg-card: #f4f4f0; + --obs-bg-well: #efefea; + --obs-border: #e8e8e4; + --obs-border-strong: #d8d8d2; + --obs-text: #1a1a1a; + --obs-text-2: #5c5c58; + --obs-text-3: #a8a8a4; + --obs-accent: #ff7f50; + --obs-accent-ink: #e05e2b; + --obs-accent-bg: #ffd7c2; + --obs-ink-dark: #111110; + + /* Infima/Docusaurus mappings */ + --ifm-background-color: var(--obs-bg); + --ifm-background-surface-color: var(--obs-bg-card); + --ifm-color-primary: var(--obs-accent-ink); + --ifm-color-primary-dark: #ca5326; + --ifm-color-primary-darker: #bf4e24; + --ifm-color-primary-darkest: #9d401d; + --ifm-color-primary-light: #ff7f50; + --ifm-color-primary-lighter: #ff926b; + --ifm-color-primary-lightest: #ffd7c2; + --ifm-font-family-base: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif; --ifm-heading-font-family: var(--ifm-font-family-base); + --ifm-font-family-monospace: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; --ifm-font-size-base: 16px; - --ifm-line-height-base: 1.65; - - /* Spacing */ - --ifm-spacing-horizontal: 1.5rem; - --ifm-spacing-vertical: 1.5rem; - - /* Border radius */ - --ifm-global-radius: 0.5rem; - --ifm-card-border-radius: 0.75rem; - - /* Shadows */ - --ifm-global-shadow-lw: 0 1px 2px 0 rgba(0, 0, 0, 0.05); - --ifm-global-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); - --ifm-global-shadow-tl: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); -} - -/* For readability concerns, you should choose a lighter palette in dark mode. */ + --ifm-line-height-base: 1.7; + --ifm-heading-color: var(--obs-text); + --ifm-font-color-base: var(--obs-text); + --ifm-font-color-secondary: var(--obs-text-2); + --ifm-link-color: var(--obs-accent-ink); + --ifm-navbar-height: 60px; + --ifm-navbar-background-color: rgba(250, 250, 248, 0.92); + --ifm-navbar-link-color: var(--obs-text-2); + --ifm-navbar-link-hover-color: var(--obs-text); + --ifm-toc-border-color: var(--obs-border); + --ifm-menu-color: var(--obs-text-2); + --ifm-menu-color-active: var(--obs-accent-ink); + --ifm-menu-color-background-active: var(--obs-accent-bg); + --ifm-code-background: var(--obs-bg-card); + --ifm-code-font-size: 0.86em; + --ifm-pre-background: #17171a; + --ifm-global-radius: 10px; + --ifm-card-border-radius: 12px; + --docusaurus-highlighted-code-line-bg: rgba(255, 127, 80, 0.12); +} + [data-theme='dark'] { - --ifm-color-primary: #818cf8; - --ifm-color-primary-dark: #6366f1; - --ifm-color-primary-darker: #4f46e5; - --ifm-color-primary-darkest: #4338ca; - --ifm-color-primary-light: #a5b4fc; - --ifm-color-primary-lighter: #c7d2fe; - --ifm-color-primary-lightest: #e0e7ff; - --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); - - --ifm-background-color: #0d1117; - --ifm-background-surface-color: #161b22; -} - -/* Enhanced navbar */ + --obs-bg: #0e0e10; + --obs-bg-card: #17171a; + --obs-bg-well: #1c1c20; + --obs-border: #26262b; + --obs-border-strong: #34343a; + --obs-text: #ecece8; + --obs-text-2: #a4a4a0; + --obs-text-3: #6a6a66; + --obs-accent: #ff8e63; + --obs-accent-ink: #ffb596; + --obs-accent-bg: rgba(255, 142, 99, 0.16); + --ifm-navbar-background-color: rgba(14, 14, 16, 0.92); + --ifm-pre-background: #1a1a1e; + --docusaurus-highlighted-code-line-bg: rgba(255, 142, 99, 0.16); +} + +html, +body { + background: var(--obs-bg); +} + +::selection { + background: var(--obs-accent-bg); +} + +/* Top bar */ .navbar { - box-shadow: var(--ifm-global-shadow-lw); - padding: 0.75rem 0; + border-bottom: 1px solid var(--obs-border); + box-shadow: none; + backdrop-filter: blur(14px); + padding: 0 28px; +} + +.navbar__brand { + gap: 12px; + margin-right: 32px; +} + +.navbar__logo { + height: 24px; +} + +.navbar__title { + color: var(--obs-text); + font-size: 16px; + font-weight: 700; + letter-spacing: -0.01em; + text-transform: lowercase; } -/* Sidebar enhancements */ +.navbar__title::after { + content: 'DOCS'; + display: inline-block; + margin-left: 10px; + padding: 2px 7px; + border-radius: 4px; + background: var(--obs-accent-bg); + color: var(--obs-accent-ink); + font-family: var(--ifm-font-family-monospace); + font-size: 11px; + font-weight: 500; + letter-spacing: 0.04em; + vertical-align: 1px; +} + +.navbar__link { + font-size: 14px; + font-weight: 400; +} + +.navbar__link--active, +.navbar__link:hover { + color: var(--obs-text); +} + +/* Left docs sidebar */ .theme-doc-sidebar-container { - border-right: 1px solid var(--ifm-toc-border-color); + width: 280px !important; + border-right: 1px solid var(--obs-border) !important; + background: var(--obs-bg); +} + +.theme-doc-sidebar-menu { + padding: 28px 18px 32px 18px; +} + +.menu { + font-size: 14px; + font-weight: 400; +} + +.menu__list .menu__list { + margin-top: 4px; + padding-left: 10px; + border-left: 1px solid var(--obs-border); +} + +.menu__list-item:not(:first-child) { + margin-top: 3px; +} + +.menu__link, +.menu__caret { + border-radius: 8px; } .menu__link { - border-radius: var(--ifm-global-radius); - transition: all 0.2s ease; + color: var(--obs-text-2); + padding: 7px 10px; + line-height: 1.35; + transition: background 120ms ease, color 120ms ease; } .menu__link:hover { - background: var(--ifm-hover-overlay); + background: var(--obs-bg-card); + color: var(--obs-text); } -.menu__link--active { - font-weight: 600; +.menu__link--active:not(.menu__link--sublist) { + background: var(--obs-accent-bg); + color: var(--obs-accent-ink); + font-weight: 500; +} + +.menu__link--sublist, +.menu__list-item-collapsible > .menu__link { + color: var(--obs-text); + font-weight: 500; } -/* Content enhancements */ -article { - max-width: 100%; +.menu__caret::before, +.menu__link--sublist-caret::after { + opacity: 0.45; +} + +/* Main documentation canvas */ +.main-wrapper { + background: var(--obs-bg); +} + +.theme-doc-markdown.markdown { + max-width: 760px; +} + +.theme-doc-markdown header + *, +.theme-doc-markdown > :first-child { + margin-top: 0; +} + +.markdown { + color: var(--obs-text-2); + font-size: 15.5px; +} + +.markdown h1, +.markdown h2, +.markdown h3, +.markdown h4 { + color: var(--obs-text); + letter-spacing: -0.025em; } .markdown h1:first-child { @@ -85,55 +219,1703 @@ article { } .markdown h1 { - font-size: 2.5rem; + font-size: 36px; + line-height: 1.12; + font-weight: 600; + margin-bottom: 18px; } .markdown h2 { - font-size: 2rem; - margin-top: 2rem; + border-top: 1px solid var(--obs-border); + font-size: 24px; + line-height: 1.25; + font-weight: 600; + margin-top: 44px; + padding-top: 32px; } .markdown h3 { - font-size: 1.5rem; + font-size: 17px; + font-weight: 600; + margin-top: 28px; +} + +.markdown p, +.markdown li { + line-height: 1.75; +} + +.markdown a:not(.hash-link) { + border-bottom: 1px solid color-mix(in srgb, var(--obs-accent-ink) 35%, transparent); + text-decoration: none; +} + +.markdown a:not(.hash-link):hover { + border-bottom-color: var(--obs-accent-ink); } -/* Table styling */ +.markdown code { + border: 1px solid var(--obs-border); + border-radius: 5px; + color: var(--obs-text); + font-family: var(--ifm-font-family-monospace); + font-size: 13.5px; + padding: 0.12em 0.36em; +} + +.theme-code-block, +pre { + border: 1px solid #2a2a30; + border-radius: 12px; + box-shadow: none; +} + +.theme-code-block pre, +pre code { + font-family: var(--ifm-font-family-monospace); + font-size: 13px; +} + +/* Tables/cards/admonitions */ table { display: table; width: 100%; - margin: 1.5rem 0; + overflow: hidden; + border: 1px solid var(--obs-border); + border-radius: 12px; + background: var(--obs-bg-card); + font-size: 14px; } -/* Code blocks */ -pre { - border-radius: var(--ifm-global-radius); +thead { + background: var(--obs-bg-well); } -/* Buttons */ -.button { - border-radius: var(--ifm-global-radius); - font-weight: 500; - transition: all 0.2s ease; +tr { + border-color: var(--obs-border); +} + +th, +td { + border-color: var(--obs-border); +} + +.card, +.pagination-nav__link, +.admonition { + border: 1px solid var(--obs-border); + border-radius: 12px; + background: var(--obs-bg-card); + box-shadow: none; +} + +.card, +.pagination-nav__link { + transition: transform 140ms ease, border-color 140ms ease, background 140ms ease; } -.button:hover { +.card:hover, +.pagination-nav__link:hover { transform: translateY(-1px); - box-shadow: var(--ifm-global-shadow-md); + border-color: var(--obs-border-strong); + background: color-mix(in srgb, var(--obs-text) 3%, var(--obs-bg-card)); +} + +.alert--info, +.alert--secondary { + --ifm-alert-background-color: color-mix(in srgb, #2a6fdb 8%, var(--obs-bg-card)); + --ifm-alert-border-color: color-mix(in srgb, #2a6fdb 24%, var(--obs-border)); +} + +.alert--success { + --ifm-alert-background-color: color-mix(in srgb, var(--obs-accent) 8%, var(--obs-bg-card)); + --ifm-alert-border-color: color-mix(in srgb, var(--obs-accent) 28%, var(--obs-border)); +} + +.alert--warning { + --ifm-alert-background-color: color-mix(in srgb, #b98429 10%, var(--obs-bg-card)); + --ifm-alert-border-color: color-mix(in srgb, #b98429 30%, var(--obs-border)); +} + +/* Right on-page TOC */ +.table-of-contents { + border-left: 1px solid var(--obs-border); + font-size: 13px; +} + +.table-of-contents__link { + color: var(--obs-text-2); } -/* Cards */ -.card { - border-radius: var(--ifm-card-border-radius); - box-shadow: var(--ifm-global-shadow-lw); - transition: all 0.2s ease; +.table-of-contents__link:hover, +.table-of-contents__link--active { + color: var(--obs-accent-ink); } -.card:hover { - box-shadow: var(--ifm-global-shadow-md); +.theme-doc-toc-desktop { + top: calc(var(--ifm-navbar-height) + 24px); } /* Footer */ .footer { - background-color: var(--ifm-background-surface-color); - border-top: 1px solid var(--ifm-toc-border-color); + background: var(--obs-ink-dark); + border-top: 1px solid var(--obs-border); +} + +.footer, +.footer a, +.footer__title { + color: #ecece8; +} + +@media (max-width: 996px) { + .navbar { + padding: 0 14px; + } + + .theme-doc-markdown.markdown { + max-width: none; + } +} + + +/* ======================================== + Original OBSRVR mock design classes + Adapted for Docusaurus pages/components + ======================================== */ + +/* OBSRVR — Design System Tokens + * Drop this into any new file. Tokens cover colors, type, spacing, + * radii, shadows. Mirrors what's in styles.css + prism-*.css. + */ + +:root { + /* ---------- Foundation ---------- */ + --bg: #FAFAF8; /* page bg — warm off-white */ + --bg-card: #F4F4F0; /* card / well */ + --bg-well: #EFEFEA; /* deeper inset, code wells */ + --border: #E8E8E4; /* hairline */ + --border-strong: #D8D8D2; + --ink-dark: #111110; /* dark sections, footer, code */ + + /* ---------- Ink ---------- */ + --text: #1A1A1A; + --text-2: #5C5C58; + --text-3: #A8A8A4; + + /* ---------- Accent ---------- */ + --accent: #FF7F50; /* coral */ + --accent-ink: #E05E2B; /* darker coral, hover + on-light text */ + --accent-bg: #FFD7C2; /* subtle wash, ::selection */ + + /* ---------- Supporting brand colors ---------- */ + --teal: #1F4E5F; /* soroban / contract */ + --violet: #5E4FA8; /* account / role */ + --amber: #B98429; /* XLM / asset */ + --green: #357A4E; /* confirmed / live */ + --red: #B83A2B; /* error / failed */ + + /* ---------- Code (dark inverted) ---------- */ + --code-bg: #17171A; + --code-fg: #E6E6E1; + --code-muted: #7A7A78; + --code-string: #E8B86E; + --code-keyword: #9CB4D8; + --code-fn: #C8A3E3; + + /* ---------- Type ---------- */ + --ff-sans: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif; + --ff-mono: "IBM Plex Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; + --ff-serif: "Source Serif 4", "Iowan Old Style", Georgia, serif; + + --fs-h1: clamp(42px, 6.2vw, 76px); + --fs-h2: clamp(30px, 3.6vw, 42px); + --fs-h3: 22px; + --fs-lead: 19px; + --fs-body: 17px; + --fs-small: 13.5px; + --fs-eyebrow: 12px; + + --lh-tight: 1.02; + --lh-snug: 1.25; + --lh-body: 1.65; + + --tracking-eyebrow: 0.12em; + --tracking-tight: -0.03em; + + /* ---------- Layout ---------- */ + --max: 1240px; + --max-prose: 960px; + --pad-section: 120px; + --gutter: 32px; + + /* ---------- Radii ---------- */ + --radius-sm: 6px; + --radius: 8px; + --radius-lg: 12px; + --radius-pill: 999px; + + /* ---------- Shadows ---------- */ + --shadow-card-hover: 0 24px 40px -28px rgba(20, 20, 18, 0.18); + --shadow-hero: 0 30px 60px -40px rgba(20, 20, 18, 0.15); + --shadow-dropdown: 0 18px 40px -18px rgba(20, 20, 18, 0.12); + --ring-accent: 0 0 0 3px rgba(255, 127, 80, 0.18); + + /* ---------- Motion ---------- */ + --t-fast: 120ms ease; + --t: 160ms ease; + --t-slow: 200ms ease; +} + +/* ---------- Semantic primitives ---------- */ +body { + font-family: var(--ff-sans); + background: var(--bg); + color: var(--text); + font-size: var(--fs-body); + line-height: var(--lh-body); + -webkit-font-smoothing: antialiased; +} +::selection { background: var(--accent-bg); color: var(--ink-dark); } + +.h1 { font-size: var(--fs-h1); line-height: var(--lh-tight); letter-spacing: var(--tracking-tight); font-weight: 600; margin: 0; text-wrap: balance; } +.h2 { font-size: var(--fs-h2); line-height: 1.1; letter-spacing: -0.02em; font-weight: 600; margin: 0; text-wrap: balance; } +.h3 { font-size: var(--fs-h3); line-height: var(--lh-snug); letter-spacing: -0.01em; font-weight: 600; margin: 0; } +.lead { font-size: var(--fs-lead); line-height: 1.55; color: var(--text-2); max-width: 640px; text-wrap: pretty; } +.eyebrow { + font-family: var(--ff-mono); + font-size: var(--fs-eyebrow); + font-weight: 500; + letter-spacing: var(--tracking-eyebrow); + text-transform: uppercase; + color: var(--text-2); +} +.eyebrow.coral { color: var(--accent-ink); } +.mono { font-family: var(--ff-mono); } + + + +[data-theme='dark'] { + --bg: #0E0E10; + --bg-card: #17171A; + --bg-well: #1C1C20; + --border: #26262B; + --border-strong: #34343A; + --ink-dark: #07070A; + --text: #ECECE8; + --text-2: #A4A4A0; + --text-3: #6A6A66; + --accent: #FF8E63; + --accent-ink: #FFB596; + --accent-bg: rgba(255, 142, 99, 0.16); + --code-bg: #1A1A1E; + --code-fg: #E6E6E1; +} + +/* Docs site — hybrid Stripe structure / Mintlify warmth */ + +:root { + --docs-side-w: 280px; + --docs-toc-w: 240px; + --docs-code-w: 480px; + --docs-top-h: 60px; + --docs-subnav-h: 48px; +} + +/* ============== Theme tokens ============== */ +body[data-doc-theme="dark"] { + --bg: #0E0E10; + --bg-card: #17171A; + --bg-well: #1C1C20; + --border: #26262B; + --border-strong: #34343A; + --ink-dark: #07070A; + --text: #ECECE8; + --text-2: #A4A4A0; + --text-3: #6A6A66; + --accent: #FF8E63; + --accent-ink: #FFB596; + --accent-bg: rgba(255, 142, 99, 0.16); + --code-bg: #1A1A1E; + --code-fg: #E6E6E1; + --shadow-card-hover: 0 24px 40px -28px rgba(0, 0, 0, 0.6); + --shadow-dropdown: 0 18px 40px -18px rgba(0, 0, 0, 0.7); +} + +html, body { background: var(--bg); } +body { font-family: var(--ff-sans); color: var(--text); margin: 0; } +* { box-sizing: border-box; } +a { color: inherit; text-decoration: none; } + +/* ============== Top bar ============== */ +.docs-topbar { + position: sticky; top: 0; z-index: 30; + height: var(--docs-top-h); + background: color-mix(in srgb, var(--bg) 88%, transparent); + backdrop-filter: saturate(140%) blur(10px); + -webkit-backdrop-filter: saturate(140%) blur(10px); + border-bottom: 1px solid var(--border); + display: flex; align-items: center; + padding: 0 28px; + gap: 28px; +} +.docs-topbar .brand { + display: flex; align-items: center; gap: 10px; + font-weight: 700; letter-spacing: -0.01em; font-size: 16px; + color: var(--text); +} +.docs-topbar .brand-mark { + width: 22px; height: 22px; border-radius: 5px; + background: var(--ink-dark); + display: grid; place-items: center; + position: relative; +} +.docs-topbar .brand-mark::before, +.docs-topbar .brand-mark::after { + content: ""; position: absolute; background: var(--accent); +} +.docs-topbar .brand-mark::before { width: 8px; height: 8px; border-radius: 50%; top: 4px; left: 4px; } +.docs-topbar .brand-mark::after { width: 1px; height: 10px; bottom: 3px; right: 5px; box-shadow: -4px 0 0 0 var(--accent); } +.docs-topbar .docs-tag { + font-family: var(--ff-mono); + font-size: 11.5px; + letter-spacing: 0.08em; + text-transform: uppercase; + padding: 3px 8px; + border-radius: 4px; + background: var(--accent-bg); + color: var(--accent-ink); + margin-left: -16px; +} + +.docs-topbar .top-links { + display: flex; gap: 22px; font-size: 14px; color: var(--text-2); + margin-left: 8px; +} +.docs-topbar .top-links a { transition: color var(--t); } +.docs-topbar .top-links a:hover, .docs-topbar .top-links a.is-active { color: var(--text); } +.docs-topbar .top-links a.is-active { font-weight: 500; } + +.docs-spacer { flex: 1; } + +/* Search command bar */ +.docs-search { + display: flex; align-items: center; gap: 10px; + height: 36px; width: 360px; max-width: 36vw; + padding: 0 12px; + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: 8px; + color: var(--text-3); + font-size: 13.5px; + cursor: pointer; + transition: all var(--t); +} +.docs-search:hover { border-color: var(--border-strong); color: var(--text-2); } +.docs-search .search-icon { color: var(--text-3); flex-shrink: 0; } +.docs-search .ask-ai { color: var(--accent-ink); font-weight: 500; } +.docs-search .kbd { + margin-left: auto; + display: inline-flex; align-items: center; gap: 2px; + font-family: var(--ff-mono); + font-size: 11px; + padding: 2px 6px; + border: 1px solid var(--border); + border-radius: 4px; + background: var(--bg); + color: var(--text-3); +} + +.theme-toggle, .docs-icon-btn { + display: inline-flex; align-items: center; justify-content: center; + width: 36px; height: 36px; + border: 1px solid var(--border); + background: var(--bg-card); + border-radius: 8px; + color: var(--text-2); + cursor: pointer; + transition: all var(--t); +} +.theme-toggle:hover, .docs-icon-btn:hover { color: var(--text); border-color: var(--border-strong); } + +.top-cta { + display: inline-flex; align-items: center; gap: 6px; + height: 36px; padding: 0 14px; + background: var(--accent); color: #fff; + border-radius: 8px; font-size: 13.5px; font-weight: 500; + transition: background var(--t); +} +.top-cta:hover { background: var(--accent-ink); } + +/* ============== Sub nav (product tabs) ============== */ +.docs-subnav { + position: sticky; top: var(--docs-top-h); z-index: 25; + height: var(--docs-subnav-h); + background: var(--bg); + border-bottom: 1px solid var(--border); + display: flex; align-items: center; + padding: 0 28px; + gap: 26px; + font-size: 14px; + overflow-x: auto; +} +.docs-subnav a { + position: relative; + display: inline-flex; align-items: center; gap: 8px; + height: 100%; + color: var(--text-2); + white-space: nowrap; + transition: color var(--t); +} +.docs-subnav a:hover { color: var(--text); } +.docs-subnav a.is-active { color: var(--text); font-weight: 500; } +.docs-subnav a.is-active::after { + content: ""; position: absolute; left: 0; right: 0; bottom: -1px; + height: 2px; background: var(--accent); border-radius: 1px 1px 0 0; +} +.docs-subnav .icon-dot { + width: 16px; height: 16px; border-radius: 4px; + background: var(--bg-card); border: 1px solid var(--border); + display: grid; place-items: center; +} + +/* ============== Layout ============== */ +.docs-shell { + display: grid; + grid-template-columns: var(--docs-side-w) minmax(0, 1fr); + align-items: start; + max-width: 1480px; + margin: 0 auto; + padding: 0 28px; +} + +/* Sidebar */ +.docs-sidebar { + position: sticky; + top: calc(var(--docs-top-h) + var(--docs-subnav-h)); + height: calc(100vh - var(--docs-top-h) - var(--docs-subnav-h)); + overflow-y: auto; + padding: 32px 24px 80px 0; + border-right: 1px solid var(--border); + font-size: 14px; +} +.docs-sidebar::-webkit-scrollbar { width: 6px; } +.docs-sidebar::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 3px; } + +.side-section { margin-bottom: 24px; } +.side-section h6 { + font-family: var(--ff-mono); + font-size: 11px; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--text-3); + margin: 0 0 10px 0; + padding: 0 10px; + font-weight: 500; +} +.side-section.collapsible h6 { + display: flex; align-items: center; gap: 6px; + cursor: pointer; user-select: none; +} +.side-section.collapsible h6 .caret { transition: transform var(--t); } +.side-section.collapsed h6 .caret { transform: rotate(-90deg); } + +.side-link { + display: flex; align-items: center; gap: 10px; + padding: 6px 10px; + border-radius: 6px; + color: var(--text-2); + cursor: pointer; + transition: all var(--t); + line-height: 1.4; +} +.side-link:hover { background: var(--bg-card); color: var(--text); } +.side-link.is-active { + background: var(--accent-bg); + color: var(--accent-ink); + font-weight: 500; +} +.side-link .ico { + width: 14px; height: 14px; opacity: 0.7; + flex-shrink: 0; +} +.side-link.is-active .ico { opacity: 1; } +.side-link.has-method { padding-left: 10px; gap: 8px; } +.method { + font-family: var(--ff-mono); + font-size: 10.5px; + font-weight: 600; + letter-spacing: 0.04em; + padding: 1px 5px; + border-radius: 3px; + flex-shrink: 0; + width: 38px; text-align: center; +} +.method.get { background: rgba(53, 122, 78, 0.14); color: #357A4E; } +.method.post { background: rgba(255, 127, 80, 0.16); color: var(--accent-ink); } +.method.del { background: rgba(184, 58, 43, 0.14); color: #B83A2B; } +.method.ws { background: rgba(94, 79, 168, 0.16); color: #7A6BC8; } + +body[data-doc-theme="dark"] .method.get { background: rgba(80, 180, 120, 0.16); color: #6FD498; } +body[data-doc-theme="dark"] .method.post { background: rgba(255, 142, 99, 0.18); color: #FFB596; } +body[data-doc-theme="dark"] .method.ws { background: rgba(140, 120, 220, 0.18); color: #B3A4F0; } + +.side-children { + margin-left: 10px; + padding-left: 12px; + border-left: 1px solid var(--border); + margin-top: 4px; +} + +/* ============== Density toggle ============== */ +body[data-density="compact"] { + --docs-side-w: 240px; +} +body[data-density="compact"] .side-link { + padding: 4px 8px; + font-size: 13px; +} +body[data-density="compact"] .side-section { margin-bottom: 16px; } +body[data-density="compact"] .side-section h6 { margin-bottom: 6px; font-size: 10.5px; } + +/* ============== Main content ============== */ +.docs-main { + display: grid; + grid-template-columns: minmax(0, 1fr) var(--docs-code-w); + gap: 56px; + padding: 40px 0 80px 56px; + align-items: start; +} + +.docs-prose { + max-width: 720px; +} + +.crumbs { + display: flex; align-items: center; gap: 6px; + font-size: 13px; + color: var(--text-3); + margin-bottom: 16px; +} +.crumbs a { color: var(--text-2); transition: color var(--t); } +.crumbs a:hover { color: var(--text); } +.crumbs .sep { color: var(--text-3); } + +.docs-prose h1 { + font-size: 36px; + letter-spacing: -0.025em; + font-weight: 600; + line-height: 1.1; + margin: 0 0 14px 0; + color: var(--text); + text-wrap: balance; +} +.docs-prose .lede { + font-size: 18px; + line-height: 1.55; + color: var(--text-2); + margin: 0 0 36px 0; + text-wrap: pretty; +} +.docs-prose h2 { + font-size: 24px; + letter-spacing: -0.015em; + font-weight: 600; + margin: 56px 0 14px 0; + scroll-margin-top: 140px; + color: var(--text); +} +.docs-prose h3 { + font-size: 17px; + font-weight: 600; + margin: 32px 0 10px 0; + scroll-margin-top: 140px; + color: var(--text); +} +.docs-prose p { + font-size: 15.5px; + line-height: 1.7; + color: var(--text); + margin: 0 0 14px 0; +} +.docs-prose p .muted { color: var(--text-2); } +.docs-prose code { + font-family: var(--ff-mono); + font-size: 13.5px; + padding: 1px 6px; + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: 4px; + color: var(--accent-ink); +} +.docs-prose a:not(.btn):not(.card-link) { + color: var(--accent-ink); + border-bottom: 1px solid color-mix(in srgb, var(--accent-ink) 30%, transparent); + transition: border-color var(--t); +} +.docs-prose a:not(.btn):not(.card-link):hover { border-bottom-color: var(--accent-ink); } +.docs-prose ul { + font-size: 15.5px; + line-height: 1.7; + padding-left: 0; + list-style: none; + margin: 0 0 14px 0; +} +.docs-prose ul li { + position: relative; + padding-left: 18px; + margin-bottom: 4px; +} +.docs-prose ul li::before { + content: ""; position: absolute; left: 4px; top: 11px; + width: 5px; height: 5px; border-radius: 50%; + background: var(--text-3); +} + +/* Callouts */ +.callout { + display: grid; + grid-template-columns: 22px 1fr; + gap: 12px; + padding: 14px 16px; + margin: 20px 0; + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: 8px; + font-size: 14.5px; + line-height: 1.6; +} +.callout .ico { color: var(--text-2); margin-top: 2px; } +.callout.note { background: color-mix(in srgb, #2A6FDB 8%, var(--bg-card)); border-color: color-mix(in srgb, #2A6FDB 24%, var(--border)); } +.callout.note .ico { color: #2A6FDB; } +.callout.tip { background: color-mix(in srgb, var(--accent) 8%, var(--bg-card)); border-color: color-mix(in srgb, var(--accent) 28%, var(--border)); } +.callout.tip .ico { color: var(--accent-ink); } +.callout.warn { background: color-mix(in srgb, #B98429 10%, var(--bg-card)); border-color: color-mix(in srgb, #B98429 30%, var(--border)); } +.callout.warn .ico { color: #B98429; } +.callout strong { color: var(--text); font-weight: 600; } + +/* Card row */ +.card-row { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 12px; + margin: 28px 0 8px 0; +} +.doc-card { + display: block; + padding: 16px 18px; + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: 10px; + transition: all var(--t); +} +.doc-card:hover { + border-color: var(--border-strong); + transform: translateY(-2px); + box-shadow: var(--shadow-card-hover); +} +.doc-card .ico-frame { + width: 28px; height: 28px; + display: grid; place-items: center; + background: var(--bg); + border: 1px solid var(--border); + border-radius: 6px; + color: var(--accent-ink); + margin-bottom: 12px; +} +.doc-card .t { font-weight: 600; font-size: 14.5px; color: var(--text); margin-bottom: 4px; } +.doc-card .d { font-size: 13.5px; color: var(--text-2); line-height: 1.5; } + +/* Param table */ +.param-table { + margin: 18px 0; + border: 1px solid var(--border); + border-radius: 8px; + background: var(--bg-card); + overflow: hidden; +} +.param-row { + display: grid; + border-bottom: 1px solid var(--border); + transition: background var(--t); +} +.param-row:last-child { border-bottom: none; } +.param-head { + display: grid; + grid-template-columns: 1fr auto auto; + gap: 14px; + align-items: baseline; + padding: 14px 16px; + cursor: pointer; + user-select: none; +} +.param-row:hover .param-head { background: color-mix(in srgb, var(--text) 3%, var(--bg-card)); } +.param-name { + font-family: var(--ff-mono); + font-size: 13.5px; + font-weight: 500; + color: var(--text); +} +.param-name .req { color: var(--accent-ink); margin-left: 4px; font-weight: 500; } +.param-type { + font-family: var(--ff-mono); + font-size: 12.5px; + color: var(--text-2); +} +.param-row .caret { + color: var(--text-3); + transition: transform var(--t); +} +.param-row.is-open .caret { transform: rotate(90deg); } +.param-desc { + padding: 0 16px 14px 16px; + font-size: 14px; + line-height: 1.6; + color: var(--text-2); + display: none; +} +.param-row.is-open .param-desc { display: block; } +.param-desc .enum { + display: flex; flex-wrap: wrap; gap: 6px; + margin-top: 10px; +} +.param-desc .enum code { + font-size: 12.5px; + padding: 2px 7px; + background: var(--bg); + border: 1px solid var(--border); + color: var(--text); +} +.param-desc .nested { + margin-top: 12px; + padding: 10px 14px; + background: var(--bg); + border: 1px solid var(--border); + border-radius: 6px; +} +.param-desc .nested .nrow { + display: grid; + grid-template-columns: 1fr auto; + gap: 10px; + padding: 4px 0; + border-bottom: 1px solid var(--border); + font-size: 13px; +} +.param-desc .nested .nrow:last-child { border-bottom: none; } +.param-desc .nested .nname { font-family: var(--ff-mono); color: var(--text); } +.param-desc .nested .ntype { font-family: var(--ff-mono); color: var(--text-2); font-size: 12px; } + +/* Footer pager */ +.docs-pager { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 14px; + margin: 64px 0 0 0; + padding-top: 24px; + border-top: 1px solid var(--border); +} +.pager-card { + display: block; + padding: 16px 18px; + background: transparent; + border: 1px solid var(--border); + border-radius: 10px; + transition: all var(--t); +} +.pager-card:hover { border-color: var(--border-strong); background: var(--bg-card); } +.pager-card .lab { + font-family: var(--ff-mono); + font-size: 11px; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--text-3); + margin-bottom: 4px; + display: flex; align-items: center; gap: 6px; +} +.pager-card .lab.right { justify-content: flex-end; } +.pager-card .ti { font-size: 14.5px; font-weight: 600; color: var(--text); } +.pager-card.next { text-align: right; } + +.docs-meta-foot { + display: flex; + justify-content: space-between; + margin-top: 20px; + font-size: 13px; + color: var(--text-3); +} +.docs-meta-foot a { color: var(--text-2); } +.docs-meta-foot a:hover { color: var(--text); } + +/* ============== Code panel (right rail) ============== */ +.docs-coderail { + position: sticky; + top: calc(var(--docs-top-h) + var(--docs-subnav-h) + 24px); + display: flex; + flex-direction: column; + gap: 16px; + align-self: start; +} + +.code-card { + background: var(--code-bg); + border: 1px solid #2A2A30; + border-radius: 10px; + overflow: hidden; + font-family: var(--ff-mono); + font-size: 13px; + color: var(--code-fg); +} +body[data-doc-theme="dark"] .code-card { border-color: #2E2E34; } + +.code-card .head { + display: flex; align-items: center; + height: 38px; + padding: 0 12px; + background: #0F0F12; + border-bottom: 1px solid #2A2A30; + font-size: 12.5px; + color: #9A9A96; + gap: 16px; +} +.code-card .head .endpoint { + display: flex; align-items: center; gap: 8px; + font-family: var(--ff-mono); + font-size: 12.5px; + flex: 1; min-width: 0; +} +.code-card .head .endpoint .meth { color: #6FD498; font-weight: 600; } +.code-card .head .endpoint .meth.ws { color: #B3A4F0; } +.code-card .head .path { + color: #E6E6E1; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; +} +.code-card .head .copy { + display: inline-flex; align-items: center; gap: 4px; + background: transparent; border: none; cursor: pointer; + color: #9A9A96; font-size: 12px; font-family: var(--ff-mono); + padding: 4px 6px; border-radius: 4px; + transition: all var(--t); +} +.code-card .head .copy:hover { color: #E6E6E1; background: rgba(255,255,255,0.05); } + +.code-tabs { + display: flex; + background: #0F0F12; + border-bottom: 1px solid #2A2A30; + padding: 0 8px; +} +.code-tabs .tab { + padding: 8px 12px; + font-size: 12px; + font-family: var(--ff-mono); + color: #7A7A78; + cursor: pointer; + border-bottom: 2px solid transparent; + transition: all var(--t); + background: transparent; border-top: none; border-left: none; border-right: none; +} +.code-tabs .tab:hover { color: #E6E6E1; } +.code-tabs .tab.active { color: #E6E6E1; border-bottom-color: var(--accent); } + +.code-body { + padding: 16px 18px; + margin: 0; + white-space: pre; + overflow-x: auto; + line-height: 1.6; + color: var(--code-fg); + max-height: 420px; +} +.code-body::-webkit-scrollbar { height: 8px; width: 8px; } +.code-body::-webkit-scrollbar-thumb { background: #2A2A30; border-radius: 4px; } +.code-body .ln { color: #4A4A4E; user-select: none; display: inline-block; width: 28px; text-align: right; padding-right: 14px; } +.code-body .k { color: #9CB4D8; } +.code-body .s { color: #E8B86E; } +.code-body .n { color: #C8A3E3; } +.code-body .c { color: #5A5A56; font-style: italic; } +.code-body .f { color: #C8A3E3; } +.code-body .v { color: #E6E6E1; } +.code-body .p { color: #7A7A78; } + +/* Response viewer */ +.resp-card { padding: 0; } +.resp-tabs { + display: flex; gap: 0; + background: #0F0F12; + padding: 0 8px; + border-bottom: 1px solid #2A2A30; +} +.resp-tabs .tab { + padding: 8px 12px; + font-size: 12px; + cursor: pointer; + color: #7A7A78; + border-bottom: 2px solid transparent; + font-family: var(--ff-mono); +} +.resp-tabs .tab.active { color: #6FD498; border-bottom-color: #6FD498; } +.resp-tabs .tab.error { color: #7A7A78; } +.resp-tabs .tab.error.active { color: #FF8E63; border-bottom-color: #FF8E63; } +.resp-status { + display: flex; align-items: center; gap: 8px; + padding: 10px 14px; + background: #0F0F12; + border-bottom: 1px solid #2A2A30; + font-family: var(--ff-mono); + font-size: 12px; + color: #9A9A96; +} +.resp-status .dot { + width: 7px; height: 7px; border-radius: 50%; + background: #6FD498; + box-shadow: 0 0 0 3px rgba(111, 212, 152, 0.2); +} +.resp-status .ms { margin-left: auto; color: #6A6A66; } + +/* Schema viewer (inline) */ +.schema { + margin: 16px 0; + border: 1px solid var(--border); + border-radius: 8px; + background: var(--bg-card); + font-family: var(--ff-mono); + font-size: 13px; + line-height: 1.7; + color: var(--text); + padding: 14px 16px; +} +.schema .field { display: grid; grid-template-columns: 1fr auto; gap: 12px; padding: 3px 0; } +.schema .fname { color: var(--text); } +.schema .ftype { color: var(--text-2); } +.schema .indent { padding-left: 18px; border-left: 1px dashed var(--border); margin-left: 4px; } + +/* Right TOC inside code rail */ +.docs-onpage { + font-size: 13px; + padding-top: 4px; +} +.docs-onpage h6 { + font-family: var(--ff-mono); + font-size: 11px; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--text-3); + margin: 0 0 10px 0; + font-weight: 500; +} +.docs-onpage a { + display: block; + padding: 4px 10px; + color: var(--text-2); + border-left: 2px solid transparent; + transition: all var(--t); + line-height: 1.5; +} +.docs-onpage a:hover { color: var(--text); } +.docs-onpage a.is-active { color: var(--accent-ink); border-left-color: var(--accent); } +.docs-onpage a.lvl-3 { padding-left: 22px; font-size: 12.5px; } + +/* ============== Cmd-K modal ============== */ +.cmdk-overlay { + position: fixed; inset: 0; z-index: 100; + background: rgba(8, 8, 10, 0.55); + backdrop-filter: blur(4px); + display: none; align-items: flex-start; justify-content: center; + padding-top: 12vh; + animation: fadeIn 120ms ease; +} +.cmdk-overlay.open { display: flex; } +@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } +.cmdk { + width: min(640px, 92vw); + background: var(--bg); + border: 1px solid var(--border); + border-radius: 12px; + box-shadow: 0 30px 80px -20px rgba(0,0,0,0.4); + overflow: hidden; +} +.cmdk-input-wrap { + display: flex; align-items: center; gap: 12px; + padding: 16px 18px; + border-bottom: 1px solid var(--border); +} +.cmdk input { + flex: 1; border: none; outline: none; background: transparent; + font-size: 15px; color: var(--text); + font-family: var(--ff-sans); +} +.cmdk input::placeholder { color: var(--text-3); } +.cmdk-results { max-height: 50vh; overflow-y: auto; padding: 8px; } +.cmdk-section h6 { + font-family: var(--ff-mono); + font-size: 10.5px; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--text-3); + padding: 8px 10px 4px; + margin: 0; +} +.cmdk-item { + display: flex; align-items: center; gap: 10px; + padding: 9px 10px; + border-radius: 6px; + cursor: pointer; + font-size: 14px; +} +.cmdk-item:hover, .cmdk-item.active { background: var(--accent-bg); color: var(--accent-ink); } +.cmdk-item .ico { color: var(--text-3); flex-shrink: 0; } +.cmdk-item:hover .ico, .cmdk-item.active .ico { color: var(--accent-ink); } +.cmdk-item .meta { margin-left: auto; font-family: var(--ff-mono); font-size: 11px; color: var(--text-3); } +.cmdk-foot { + display: flex; align-items: center; gap: 14px; + padding: 10px 16px; + border-top: 1px solid var(--border); + background: var(--bg-card); + font-size: 12px; color: var(--text-3); +} +.cmdk-foot .kbd { font-family: var(--ff-mono); font-size: 10.5px; padding: 1px 5px; background: var(--bg); border: 1px solid var(--border); border-radius: 3px; color: var(--text-2); margin-right: 4px; } + +/* ============== Tweaks panel adjust ============== */ +.tweaks-anchor { position: fixed; bottom: 16px; right: 16px; z-index: 50; } + +@media (max-width: 1280px) { + .docs-main { grid-template-columns: minmax(0, 1fr); } + .docs-coderail { position: static; } +} +@media (max-width: 980px) { + .docs-shell { grid-template-columns: 1fr; } + .docs-sidebar { display: none; } + .docs-main { padding: 32px 0; } +} + + + +/* Docs landing-page styles — sits on top of styles-docs.css */ + +/* Landing has chrome but no left sidebar; content is a centered column */ +.landing-shell { + max-width: 1200px; + margin: 0 auto; + padding: 0 28px 80px; +} + +/* ============== Hero ============== */ +.dl-hero { + padding: 64px 0 48px; + position: relative; +} +.dl-hero::before { + content: ""; position: absolute; + inset: 0; + background: radial-gradient(800px 320px at 20% 0%, color-mix(in srgb, var(--accent) 9%, transparent), transparent 60%); + pointer-events: none; + z-index: -1; +} +.dl-hero .pill { + display: inline-flex; align-items: center; gap: 8px; + padding: 5px 11px; + border: 1px solid var(--border); + border-radius: 999px; + background: var(--bg-card); + font-family: var(--ff-mono); + font-size: 11.5px; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--text-2); +} +.dl-hero .pill .dot { + width: 7px; height: 7px; border-radius: 50%; + background: #4FB87A; + box-shadow: 0 0 0 3px rgba(79, 184, 122, 0.18); + animation: dl-pulse 2.4s ease-in-out infinite; +} +@keyframes dl-pulse { + 0%, 100% { box-shadow: 0 0 0 3px rgba(79, 184, 122, 0.18); } + 50% { box-shadow: 0 0 0 5px rgba(79, 184, 122, 0.10); } +} + +.dl-hero h1 { + font-size: 52px; + letter-spacing: -0.03em; + font-weight: 600; + line-height: 1.04; + margin: 18px 0 16px; + color: var(--text); + max-width: 820px; + text-wrap: balance; +} +.dl-hero h1 .accent { color: var(--accent-ink); } +.dl-hero .lede { + font-size: 19px; + line-height: 1.55; + color: var(--text-2); + max-width: 680px; + margin: 0 0 28px; + text-wrap: pretty; +} + +/* Big search bar */ +.dl-bigsearch { + display: flex; align-items: center; gap: 14px; + padding: 0 18px; + height: 60px; + width: 100%; + max-width: 720px; + background: var(--bg-card); + border: 1px solid var(--border-strong); + border-radius: 12px; + font-size: 16px; + color: var(--text-3); + cursor: pointer; + transition: all var(--t); + box-shadow: 0 6px 18px -10px rgba(0, 0, 0, 0.08); +} +.dl-bigsearch:hover { + border-color: var(--accent); + box-shadow: 0 8px 24px -10px color-mix(in srgb, var(--accent) 25%, transparent); +} +.dl-bigsearch .sicon { + width: 22px; height: 22px; + display: grid; place-items: center; + color: var(--accent-ink); +} +.dl-bigsearch .lab { color: var(--text); font-weight: 400; } +.dl-bigsearch .ai-chip { + display: inline-flex; align-items: center; gap: 5px; + padding: 3px 9px; + background: var(--accent-bg); + color: var(--accent-ink); + border-radius: 999px; + font-size: 12.5px; + font-weight: 500; + font-family: var(--ff-sans); +} +.dl-bigsearch .kbd { + margin-left: auto; + font-family: var(--ff-mono); + font-size: 12px; + padding: 4px 8px; + border: 1px solid var(--border); + border-radius: 5px; + background: var(--bg); + color: var(--text-3); +} + +/* Hero footer hints */ +.dl-hero .hints { + display: flex; flex-wrap: wrap; gap: 6px 18px; + margin-top: 16px; + font-size: 13.5px; + color: var(--text-3); +} +.dl-hero .hints b { + font-weight: 500; + color: var(--text-2); + margin-right: 4px; +} +.dl-hero .hints a { + display: inline-flex; align-items: center; gap: 4px; + color: var(--text-2); + border-bottom: 1px dashed var(--border-strong); + transition: all var(--t); + padding-bottom: 1px; +} +.dl-hero .hints a:hover { color: var(--accent-ink); border-bottom-color: var(--accent); } + +/* ============== Section headings ============== */ +.dl-section { margin-top: 64px; } +.dl-section .head { + display: flex; align-items: baseline; gap: 14px; + margin-bottom: 22px; +} +.dl-section .head .eyebrow { + font-family: var(--ff-mono); + font-size: 11.5px; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--accent-ink); + font-weight: 500; +} +.dl-section .head h2 { + font-size: 26px; + font-weight: 600; + letter-spacing: -0.018em; + margin: 0; + color: var(--text); +} +.dl-section .head .sub { + font-size: 14.5px; + color: var(--text-2); + margin-left: auto; +} +.dl-section .head .sub a { + color: var(--accent-ink); + display: inline-flex; align-items: center; gap: 4px; +} +.dl-section .head .sub a:hover { text-decoration: underline; } + +/* ============== Quickstart split ============== */ +.dl-quickstart { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); + gap: 36px; + align-items: start; +} +.dl-qs-steps { display: flex; flex-direction: column; gap: 14px; } +.dl-step { + display: grid; + grid-template-columns: 28px 1fr; + gap: 16px; + align-items: start; +} +.dl-step .num { + display: grid; place-items: center; + width: 28px; height: 28px; + border: 1px solid var(--border); + background: var(--bg-card); + border-radius: 999px; + font-family: var(--ff-mono); + font-size: 12.5px; + font-weight: 500; + color: var(--text-2); +} +.dl-step.is-active .num { + background: var(--ink-dark); + color: #fff; + border-color: var(--ink-dark); +} +.dl-step h4 { + font-size: 15.5px; + font-weight: 600; + margin: 4px 0 4px; + color: var(--text); +} +.dl-step p { + font-size: 14px; + color: var(--text-2); + margin: 0; + line-height: 1.55; +} +.dl-step code { + font-family: var(--ff-mono); + font-size: 12.5px; + padding: 1px 5px; + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: 4px; + color: var(--accent-ink); +} + +.dl-qs-code { + position: sticky; + top: calc(var(--docs-top-h) + var(--docs-subnav-h) + 24px); +} + +/* ============== Product cards ============== */ +.dl-product-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 16px; +} +.dl-prod { + display: block; + padding: 22px; + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: 12px; + transition: all var(--t); + position: relative; + overflow: hidden; +} +.dl-prod:hover { + border-color: var(--border-strong); + transform: translateY(-2px); + box-shadow: var(--shadow-card-hover); +} +.dl-prod .ic { + width: 36px; height: 36px; + display: grid; place-items: center; + background: var(--bg); + border: 1px solid var(--border); + border-radius: 8px; + color: var(--accent-ink); + margin-bottom: 14px; +} +.dl-prod h3 { + font-size: 16px; + font-weight: 600; + margin: 0 0 6px; + color: var(--text); + display: flex; align-items: center; gap: 8px; +} +.dl-prod h3 .ext { + color: var(--text-3); + transition: transform var(--t), color var(--t); +} +.dl-prod:hover h3 .ext { + color: var(--accent-ink); + transform: translate(2px, -2px); +} +.dl-prod p { + font-size: 14px; + color: var(--text-2); + line-height: 1.55; + margin: 0 0 14px; +} +.dl-prod .links { + display: flex; flex-direction: column; gap: 6px; + border-top: 1px solid var(--border); + padding-top: 12px; +} +.dl-prod .links span { + display: flex; align-items: center; gap: 8px; + font-size: 13px; + color: var(--text-2); +} +.dl-prod .links .method { + font-size: 9.5px; + width: 32px; + padding: 1px 4px; +} + +/* ============== By role ============== */ +.dl-roles { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 14px; +} +.dl-role { + padding: 22px; + background: linear-gradient(180deg, var(--bg-card) 0%, var(--bg) 100%); + border: 1px solid var(--border); + border-radius: 12px; + transition: all var(--t); + display: flex; + flex-direction: column; +} +.dl-role:hover { + border-color: var(--border-strong); + transform: translateY(-2px); + box-shadow: var(--shadow-card-hover); +} +.dl-role .glyph { + width: 44px; height: 44px; + border-radius: 10px; + display: grid; place-items: center; + margin-bottom: 16px; + background: var(--accent-bg); + color: var(--accent-ink); +} +.dl-role.b .glyph { background: color-mix(in srgb, #2A6FDB 14%, var(--bg-card)); color: #2A6FDB; } +body[data-doc-theme="dark"] .dl-role.b .glyph { color: #7BA8E6; } +.dl-role.c .glyph { background: color-mix(in srgb, #357A4E 14%, var(--bg-card)); color: #357A4E; } +body[data-doc-theme="dark"] .dl-role.c .glyph { color: #6FD498; } + +.dl-role h3 { + font-size: 17px; + font-weight: 600; + margin: 0 0 6px; + color: var(--text); +} +.dl-role p { + font-size: 14px; + color: var(--text-2); + line-height: 1.55; + margin: 0 0 16px; +} +.dl-role ul { + list-style: none; padding: 0; margin: 0 0 16px; + display: flex; flex-direction: column; gap: 6px; +} +.dl-role ul li { + display: flex; align-items: center; gap: 8px; + font-size: 13.5px; + color: var(--text-2); +} +.dl-role ul li .check { + color: var(--accent-ink); + flex-shrink: 0; +} +.dl-role .start { + margin-top: auto; + display: inline-flex; align-items: center; gap: 6px; + font-size: 13.5px; + font-weight: 500; + color: var(--accent-ink); + transition: gap var(--t); +} +.dl-role:hover .start { gap: 9px; } + +/* ============== Popular guides ============== */ +.dl-guides { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 14px; +} +.dl-guide { + display: grid; + grid-template-columns: 44px 1fr auto; + gap: 16px; + align-items: center; + padding: 16px 18px; + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: 10px; + transition: all var(--t); +} +.dl-guide:hover { + border-color: var(--border-strong); + background: color-mix(in srgb, var(--accent) 4%, var(--bg-card)); +} +.dl-guide .ic { + width: 44px; height: 44px; + display: grid; place-items: center; + background: var(--bg); + border: 1px solid var(--border); + border-radius: 8px; + color: var(--text-2); +} +.dl-guide:hover .ic { color: var(--accent-ink); border-color: var(--accent); } +.dl-guide .body .t { + font-size: 14.5px; + font-weight: 600; + color: var(--text); + margin-bottom: 2px; +} +.dl-guide .body .d { + font-size: 13px; + color: var(--text-2); + line-height: 1.5; +} +.dl-guide .body .meta { + font-family: var(--ff-mono); + font-size: 11px; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--text-3); + margin-top: 5px; +} +.dl-guide .arrow { + color: var(--text-3); + transition: all var(--t); +} +.dl-guide:hover .arrow { color: var(--accent-ink); transform: translateX(3px); } + +/* ============== Help footer band ============== */ +.dl-help { + margin-top: 80px; + padding: 36px 36px; + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: 14px; + display: grid; + grid-template-columns: 1fr auto; + gap: 24px; + align-items: center; +} +.dl-help h3 { + font-size: 20px; + font-weight: 600; + margin: 0 0 4px; + color: var(--text); +} +.dl-help p { + font-size: 14px; + color: var(--text-2); + margin: 0; +} +.dl-help .actions { + display: flex; gap: 10px; +} +.dl-help .actions a { + display: inline-flex; align-items: center; gap: 6px; + padding: 9px 14px; + border-radius: 8px; + font-size: 13.5px; + font-weight: 500; + border: 1px solid var(--border); + color: var(--text); + background: var(--bg); + transition: all var(--t); +} +.dl-help .actions a:hover { border-color: var(--border-strong); } +.dl-help .actions a.primary { + background: var(--accent); color: #fff; border-color: var(--accent); +} +.dl-help .actions a.primary:hover { background: var(--accent-ink); border-color: var(--accent-ink); } + +@media (max-width: 980px) { + .dl-quickstart { grid-template-columns: 1fr; } + .dl-qs-code { position: static; } + .dl-product-grid, .dl-roles { grid-template-columns: 1fr; } + .dl-guides { grid-template-columns: 1fr; } + .dl-hero h1 { font-size: 38px; } + .dl-help { grid-template-columns: 1fr; } +} + + + +body.obsrvr-landing-page .footer, +.obsrvr-landing-page .footer { + display: none; +} + +body.obsrvr-landing-page, +.obsrvr-landing-page { + background: var(--bg); + color: var(--text); + min-height: 100vh; +} + +.docs-topbar .brand.obsrvr-logo-brand { + min-width: 82px; +} + +.docs-topbar .brand.obsrvr-logo-brand img { + display: block; + width: 82px; + height: auto; +} + +.docs-search, .theme-toggle, .docs-icon-btn, .top-cta, .dl-bigsearch, .code-tabs .tab, .code-card .copy { cursor: pointer; } + +/* Pricing page */ +.pricing-tier-grid { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 18px; + margin: 32px 0 48px; +} + +.pricing-tier-card { + position: relative; + display: flex; + flex-direction: column; + min-height: 100%; + padding: 26px; + border: 1px solid var(--border); + border-radius: 16px; + background: var(--bg-card); +} + +.pricing-tier-card.featured { + border-color: var(--accent); + box-shadow: 0 22px 44px -32px color-mix(in srgb, var(--accent) 45%, transparent); +} + +.pricing-tier-card h3 { + margin: 0 0 8px; + color: var(--text); + font-size: 22px; + font-weight: 600; + letter-spacing: -0.02em; +} + +.pricing-tier-sub { + min-height: 48px; + margin: 0 0 22px; + color: var(--text-2); + font-size: 14px; + line-height: 1.45; +} + +.pricing-price { + margin: 0 0 22px; + color: var(--text); + font-size: 34px; + font-weight: 600; + letter-spacing: -0.04em; +} + +.pricing-price span { + display: block; + margin-top: 4px; + color: var(--text-3); + font-family: var(--ff-mono); + font-size: 11px; + font-weight: 500; + letter-spacing: 0.06em; + text-transform: uppercase; +} + +.pricing-tier-card ul { + margin: 0 0 24px; + padding: 0; + list-style: none; +} + +.pricing-tier-card li { + padding: 8px 0; + border-top: 1px solid var(--border); + color: var(--text-2); + font-size: 14px; +} + +.pricing-tier-card li strong { + color: var(--text); +} + +.pricing-flag { + align-self: flex-start; + margin-bottom: 14px; + padding: 4px 8px; + border-radius: 999px; + background: var(--accent-bg); + color: var(--accent-ink); + font-family: var(--ff-mono); + font-size: 10px; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.pricing-button { + display: inline-flex; + align-items: center; + justify-content: center; + margin-top: auto; + padding: 10px 14px; + border-radius: 9px; + border: 1px solid var(--border); + font-size: 13.5px; + font-weight: 500; + text-decoration: none !important; + transition: all var(--t); +} + +.pricing-button.primary { + border-color: var(--accent); + background: var(--accent); + color: #fff !important; +} + +.pricing-button.primary:hover { + border-color: var(--accent-ink); + background: var(--accent-ink); +} + +.pricing-button.secondary { + background: var(--bg); + color: var(--text) !important; +} + +.pricing-button.secondary:hover { + border-color: var(--border-strong); + transform: translateY(-1px); +} + +.pricing-cta-row { + display: flex; + gap: 12px; + flex-wrap: wrap; + margin: 24px 0 12px; +} + +@media (max-width: 1200px) { + .pricing-tier-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } +} + +@media (max-width: 700px) { + .pricing-tier-grid { grid-template-columns: 1fr; } + .pricing-price { font-size: 30px; } } diff --git a/src/pages/index.js b/src/pages/index.js index b6314d1..e4c2a16 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -1,133 +1,148 @@ -import clsx from "clsx"; -import Link from "@docusaurus/Link"; -import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; -import Layout from "@theme/Layout"; -import styles from "./index.module.css"; - -function HomepageHeader() { - const { siteConfig } = useDocusaurusContext(); - return ( -
-
-

- Obsrvr Docs -

-

- Build on Stellar & Soroban with confidence -

-

- Everything you need to integrate with Obsrvr's gateway services and Flow data pipelines -

-
-
- ); +import React, {useEffect, useState} from 'react'; +import Layout from '@theme/Layout'; +import Link from '@docusaurus/Link'; + +function Ico({w = 16, children}) { + return {children}; +} + +const IL = { + search: (p) => , + sun: (p) => , + moon: (p) => , + copy: (p) => , + check: (p) => , + arrowR: (p) => , + zap: (p) => , + book: (p) => , + rocket: (p) => , + key: (p) => , + layers: (p) => , + shield: (p) => , + webhook: (p) => , + external: (p) => , + ai: (p) => , + terminal: (p) => , + github: (p) => , + wallet: (p) => , + branch: (p) => , + shieldCheck: (p) => , + filter: (p) => , + warn: (p) => , + msg: (p) => , +}; + +function Hero({onSearch}) { + return
+ All systems operational · ledger 55,812,406 +

Build on Stellar without building the data layer.

+

Decoded, queryable Stellar data — bronze to gold, with tables for token transfers, contract events, account snapshots, and network metrics.

+ +
Try:Get USDC transfers for an accountCompare Lake to raw getEventsFind Gateway endpoints
+
; +} + +const LAKE_CURL = `export API_KEY="your-api-key" +export BASE="https://gateway.withobsrvr.com/lake/v1/testnet" + +curl -H "Authorization: Api-Key $API_KEY" \ + "$BASE/api/v1/silver/transfers?asset_code=USDC&limit=10"`; + +const LAKE_SQL = `-- Same question in SQL against Lake silver tables +select + ledger_closed_at, + from_account, + to_account, + asset_code, + amount +from silver.token_transfers +where asset_code = 'USDC' +order by ledger_closed_at desc +limit 10;`; + +const LAKE_CONTRACTS = `curl -H "Authorization: Api-Key $API_KEY" \ + "$BASE/api/v1/silver/contracts/top?period=24h&limit=10" + +curl -H "Authorization: Api-Key $API_KEY" \ + "$BASE/api/v1/silver/events/generic?topic0=transfer&limit=10"`; + +const LANGS = { + curl: {label: 'Lake API', install: 'Query silver.token_transfers', code: LAKE_CURL}, + sql: {label: 'SQL', install: 'Query Lake silver tables', code: LAKE_SQL}, + contracts: {label: 'Soroban', install: 'Find active contracts and CAP-67 events', code: LAKE_CONTRACTS}, +}; + +function highlight(src) { + return src.split('\n').map((line, i) =>
{i + 1}{line}
); +} + +function Quickstart() { + const [lang, setLang] = useState('curl'); + const [copied, setCopied] = useState(false); + const active = LANGS[lang]; + const copy = async () => { + try { + await navigator.clipboard.writeText(active.code); + setCopied(true); + setTimeout(() => setCopied(false), 1200); + } catch (error) { + console.error('Failed to copy code sample', error); + } + }; + return
+
Get started

Query decoded Stellar data in under 60 seconds

Lake quickstart
+
+
    {['Get an API key', 'Query Lake', 'Use silver tables', 'Move to gold metrics'].map((title, i) =>
  1. {i + 1}

    {title}

    {['Create a key in Console and send it as Authorization: Api-Key $API_KEY.', 'Start with a REST call against Lake testnet. No Horizon joins or XDR decoding.', 'Use analytics-ready tables for transfers, accounts, contract events, and Soroban calls.', 'When you need reporting, use gold datasets for stablecoin volumes, fee intelligence, and network health.'][i]}

  2. )}
+
${active.install}
{Object.entries(LANGS).map(([id, l]) => )}
{highlight(active.code)}
+
+
; +} + +const products = [ + ['layers', 'Obsrvr Lake', 'Decoded Stellar data, queryable. Bronze, silver, and gold tables for tokens, contracts, accounts, and network metrics.', '/docs/lake/overview', ['GET Silver API', 'SQL Lake tables', 'GET Gold metrics']], + ['zap', 'Flow', 'Custom data pipelines composed from processors and sinks. Deploy managed Stellar pipelines when Lake is not enough.', '/docs/flow/overview', ['YAML Pipelines', 'POST Sinks', 'GET Processors']], + ['terminal', 'Gateway', 'Drop-in Horizon and Stellar RPC access. Use it for network calls, submission, and SDK-compatible RPC.', '/docs/gateway/overview', ['RPC Stellar', 'GET Horizon', 'API Auth']], + ['rocket', 'Get Started', 'Pick your path: query Lake, run Flow, or test Gateway. Start with Lake unless you need raw infrastructure.', '/docs/intro', ['GET Intro', 'GET Quickstart', 'GET Pricing']], +]; + +function Products() { + return
Explore by product

Lake first. Flow and Gateway when you need them.

View the platform
{products.map(([icon, title, desc, href, links]) => { const Icon = IL[icon]; return

{title}

{desc}

{links.map((x) => { const [m, ...rest] = x.split(' '); return {m}{rest.join(' ')}; })}
; })}
; } -function FeatureCard({ title, description, link, icon, gradient }) { - return ( - -
-
{icon}
-

{title}

-

{description}

-
- - ); +const roles = [ + ['a', IL.wallet, 'Wallets', 'Show users a clear, human-readable history of their on-chain activity.', ['Decoded transaction summaries', 'Asset deltas in/out per address', 'Soroban contract calls labeled'], '/docs/lake/guides/query-examples'], + ['b', IL.branch, 'Protocols', 'Read your own contract events and the rest of the network through a single API.', ['Contract event streams', 'Soroban storage snapshots', 'Cross-protocol asset routing'], '/docs/flow/overview'], + ['c', IL.shieldCheck, 'Compliance teams', 'Audit-ready exports, sanctions screening, and stablecoin flow analytics.', ['Per-address activity reports', 'Sanctioned counterparty checks', 'CSV / parquet exports'], '/docs/lake/overview'], +]; + +function ByRole() { + return
Start by role

Pick the path that fits what you're building

{roles.map(([cls, Icon, title, desc, bullets, href]) =>

{title}

{desc}

    {bullets.map((b) =>
  • {b}
  • )}
{title} guide )}
; } -function HomepageFeatures() { - const features = [ - { - title: "What is Obsrvr?", - description: "Learn about our Web3 development platform for Stellar and Soroban networks", - link: "/docs/intro", - icon: "🌐", - gradient: styles.gradientBlue, - }, - { - title: "Gateway Services", - description: "Connect to Stellar Horizon and Soroban RPC with enterprise-grade reliability", - link: "/docs/gateway/overview", - icon: "🔌", - gradient: styles.gradientPurple, - }, - { - title: "Flow Pipelines", - description: "Build data pipelines to process blockchain events with one-click deployment", - link: "/docs/flow/overview", - icon: "🔄", - gradient: styles.gradientGreen, - }, - { - title: "Get Started", - description: "Create your first Flow pipeline and start processing blockchain data", - link: "/docs/flow/getting-started/quickstart", - icon: "🚀", - gradient: styles.gradientOrange, - }, - ]; - - return ( -
-
-
- {features.map((feature, idx) => ( - - ))} -
-
-
- ); +const guides = [ + [IL.layers, 'Query token transfers', 'Get classic payments and SAC transfers from one Lake silver endpoint.', 'Lake · 3 min read', '/docs/lake/guides/query-examples'], + [IL.book, 'Understand bronze, silver, and gold', 'How raw decoded ledger data becomes analytics tables and business metrics.', 'Lake · 5 min read', '/docs/lake/overview'], + [IL.search, 'Start with the Lake quickstart', 'Run your first account, transfer, and contract-event queries.', 'Lake · 5 min read', '/docs/lake/getting-started/quickstart'], + [IL.zap, 'Build a custom Flow pipeline', 'Use Flow when you need your own processors and sinks.', 'Flow · 7 min read', '/docs/flow/overview'], + [IL.terminal, 'Use Gateway endpoints', 'Horizon and Stellar RPC URLs, auth, and SDK-compatible examples.', 'Gateway · 4 min read', '/docs/gateway/overview'], + [IL.key, 'Create an API key in Console', 'Console manages keys, subscriptions, and Obsrvr resources.', 'Console · 3 min read', 'https://console.withobsrvr.com'], +]; + +function Guides() { + return
Popular topics

Start with decoded data, then choose your tool

Browse Lake examples
{guides.map(([Icon, t, d, meta, href]) =>
{t}
{d}
{meta}
)}
; } -function QuickLinks() { - return ( -
-
-

Popular Topics

-
-
-

Getting Started

-
    -
  • Introduction
  • -
  • Flow Quickstart
  • -
  • Pricing
  • -
-
-
-

Flow Components

-
    -
  • Processors
  • -
  • Consumers
  • -
  • Pipeline Concepts
  • -
-
-
-

Resources

- -
-
-
-
- ); +function Help() { + return

Stuck or need higher limits?

Talk to a Stellar engineer at Obsrvr — same person who'd answer in production support.

Chat with usContact sales
; } export default function Home() { - const { siteConfig } = useDocusaurusContext(); - return ( - - -
- - -
-
- ); -} \ No newline at end of file + useEffect(() => { + document.body.classList.add('obsrvr-landing-page'); + return () => document.body.classList.remove('obsrvr-landing-page'); + }, []); + const openSearch = () => window.dispatchEvent(new CustomEvent('obsrvr:open-search')); + return +
+
; +} diff --git a/src/theme/DocSidebar/Desktop/Content/index.js b/src/theme/DocSidebar/Desktop/Content/index.js new file mode 100644 index 0000000..cd1d2c1 --- /dev/null +++ b/src/theme/DocSidebar/Desktop/Content/index.js @@ -0,0 +1,114 @@ +import React, {useState} from 'react'; +import clsx from 'clsx'; +import {ThemeClassNames} from '@docusaurus/theme-common'; +import { + useAnnouncementBar, + useScrollPosition, +} from '@docusaurus/theme-common/internal'; +import {translate} from '@docusaurus/Translate'; +import DocSidebarItems from '@theme/DocSidebarItems'; +import Link from '@docusaurus/Link'; +import {useLocation} from '@docusaurus/router'; +import styles from './styles.module.css'; + +// Product configuration +const products = [ + { + name: 'Gateway', + path: '/docs/gateway/', + description: 'Stellar RPC & Horizon API', + }, + { + name: 'Flow', + path: '/docs/flow/', + description: 'Real-time blockchain data', + }, + { + name: 'Lake', + path: '/docs/lake/', + description: 'Data warehouse & analytics', + }, + { + name: 'Nodes', + path: '/docs/nodes/', + description: 'Dedicated Stellar infrastructure', + }, +]; + +function useShowAnnouncementBar() { + const {isActive} = useAnnouncementBar(); + const [showAnnouncementBar, setShowAnnouncementBar] = useState(isActive); + useScrollPosition( + ({scrollY}) => { + if (isActive) { + setShowAnnouncementBar(scrollY === 0); + } + }, + [isActive], + ); + return isActive && showAnnouncementBar; +} + +function ProductSwitcher() { + const location = useLocation(); + const currentPath = location.pathname; + + // Determine which product is active + const getActiveProduct = () => { + for (const product of products) { + if (currentPath.startsWith(product.path)) { + return product.name; + } + } + return null; + }; + + const activeProduct = getActiveProduct(); + + return ( +
+ +
+ ); +} + +export default function DocSidebarDesktopContent({path, sidebar, className}) { + const showAnnouncementBar = useShowAnnouncementBar(); + return ( + + ); +} diff --git a/src/theme/DocSidebar/Desktop/Content/styles.module.css b/src/theme/DocSidebar/Desktop/Content/styles.module.css new file mode 100644 index 0000000..8e288a6 --- /dev/null +++ b/src/theme/DocSidebar/Desktop/Content/styles.module.css @@ -0,0 +1,64 @@ +@media (min-width: 997px) { + .menu { + flex-grow: 1; + padding: 0.5rem; + display: flex; + flex-direction: column; + } + @supports (scrollbar-gutter: stable) { + .menu { + padding: 0.5rem 0 0.5rem 0.5rem; + scrollbar-gutter: stable; + } + } + + .menuWithAnnouncementBar { + margin-bottom: var(--docusaurus-announcement-bar-height); + } +} + +/* Product Switcher Styles */ +.productSwitcher { + margin-top: auto; + padding-top: 1rem; + border-top: 1px solid var(--ifm-toc-border-color); +} + +.productList { + list-style: none; + margin: 0; + padding: 0; +} + +.productList li { + margin: 0; +} + +.productLink { + display: block; + padding: 0.5rem 0.75rem; + color: var(--ifm-menu-color); + font-size: 0.9rem; + text-decoration: none; + transition: all 0.15s ease; + border-radius: 4px; +} + +.productLink:hover { + color: var(--ifm-menu-color-active); + text-decoration: none; + background: var(--ifm-menu-color-background-hover); +} + +.productLinkActive { + background: var(--ifm-menu-color-background-active); + color: var(--ifm-menu-color-active); + font-weight: 500; + border-left: 3px solid var(--ifm-color-primary); + padding-left: calc(0.75rem - 3px); +} + +.productLinkActive:hover { + background: var(--ifm-menu-color-background-active); + color: var(--ifm-menu-color-active); +} diff --git a/src/theme/DocSidebar/Mobile/index.js b/src/theme/DocSidebar/Mobile/index.js new file mode 100644 index 0000000..a98ce3d --- /dev/null +++ b/src/theme/DocSidebar/Mobile/index.js @@ -0,0 +1,116 @@ +import React from 'react'; +import clsx from 'clsx'; +import { + NavbarSecondaryMenuFiller, + ThemeClassNames, +} from '@docusaurus/theme-common'; +import {useNavbarMobileSidebar} from '@docusaurus/theme-common/internal'; +import DocSidebarItems from '@theme/DocSidebarItems'; +import Link from '@docusaurus/Link'; +import {useLocation} from '@docusaurus/router'; +import styles from './styles.module.css'; + +// Product configuration (same as desktop) +const products = [ + { + name: 'Gateway', + path: '/docs/gateway/', + description: 'Stellar RPC & Horizon API', + }, + { + name: 'Flow', + path: '/docs/flow/', + description: 'Real-time blockchain data', + }, + { + name: 'Lake', + path: '/docs/lake/', + description: 'Data warehouse & analytics', + }, + { + name: 'Nodes', + path: '/docs/nodes/', + description: 'Dedicated Stellar infrastructure', + }, +]; + +function ProductSwitcherMobile({onItemClick}) { + const location = useLocation(); + const currentPath = location.pathname; + + const getActiveProduct = () => { + for (const product of products) { + if (currentPath.startsWith(product.path)) { + return product.name; + } + } + return null; + }; + + const activeProduct = getActiveProduct(); + + return ( +
+ +
+ ); +} + +// eslint-disable-next-line react/function-component-definition +const DocSidebarMobileSecondaryMenu = ({sidebar, path}) => { + const mobileSidebar = useNavbarMobileSidebar(); + return ( + <> + + mobileSidebar.toggle()} /> + + ); +}; + +function DocSidebarMobile(props) { + return ( + + ); +} + +export default React.memo(DocSidebarMobile); diff --git a/src/theme/DocSidebar/Mobile/styles.module.css b/src/theme/DocSidebar/Mobile/styles.module.css new file mode 100644 index 0000000..cd2c91e --- /dev/null +++ b/src/theme/DocSidebar/Mobile/styles.module.css @@ -0,0 +1,45 @@ +/* Product Switcher Styles for Mobile */ +.productSwitcher { + margin-top: 1rem; + padding-top: 1rem; + border-top: 1px solid var(--ifm-toc-border-color); +} + +.productList { + list-style: none; + margin: 0; + padding: 0; +} + +.productList li { + margin: 0; +} + +.productLink { + display: block; + padding: 0.5rem 0.75rem; + color: var(--ifm-menu-color); + font-size: 0.9rem; + text-decoration: none; + transition: all 0.15s ease; + border-radius: 4px; +} + +.productLink:hover { + color: var(--ifm-menu-color-active); + text-decoration: none; + background: var(--ifm-menu-color-background-hover); +} + +.productLinkActive { + background: var(--ifm-menu-color-background-active); + color: var(--ifm-menu-color-active); + font-weight: 500; + border-left: 3px solid var(--ifm-color-primary); + padding-left: calc(0.75rem - 3px); +} + +.productLinkActive:hover { + background: var(--ifm-menu-color-background-active); + color: var(--ifm-menu-color-active); +} diff --git a/src/theme/Navbar/index.js b/src/theme/Navbar/index.js new file mode 100644 index 0000000..f62d3d1 --- /dev/null +++ b/src/theme/Navbar/index.js @@ -0,0 +1,6 @@ +import React from 'react'; +import ObsrvrTopNav from '../../components/ObsrvrTopNav'; + +export default function NavbarWrapper() { + return ; +}