@forzalabs/remora 2.3.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +93 -0
- package/docs/Remora - Code Sets.md +268 -0
- package/docs/Remora - Consumers.md +2224 -0
- package/docs/Remora - Producers.md +1658 -0
- package/docs/Remora - Sources.md +1150 -0
- package/documentation/README.md +262 -133
- package/documentation/default_resources/mock_data.csv +2 -2
- package/index.js +2891 -1955
- package/json_schemas/consumer-schema.json +154 -10
- package/json_schemas/producer-schema.json +0 -38
- package/package.json +4 -2
- package/workers/ExecutorWorker.js +1327 -403
|
@@ -0,0 +1,1150 @@
|
|
|
1
|
+
# Remora - Sources
|
|
2
|
+
|
|
3
|
+
## **Overview**
|
|
4
|
+
|
|
5
|
+
Sources in Remora define the connection configurations to your data storage systems. They act as the foundation for data extraction through producers and serve as the bridge between Remora and your various data stores.
|
|
6
|
+
|
|
7
|
+
Each source configuration specifies:
|
|
8
|
+
|
|
9
|
+
- **Connection details** (host, credentials, region, etc.)
|
|
10
|
+
- **Authentication method** (how to securely connect)
|
|
11
|
+
- **Engine type** (what kind of data store it is)
|
|
12
|
+
|
|
13
|
+
## **Source Types (Engines)**
|
|
14
|
+
|
|
15
|
+
Remora supports the following source engines:
|
|
16
|
+
|
|
17
|
+
| Engine | Description | Use Cases |
|
|
18
|
+
| --- | --- | --- |
|
|
19
|
+
| `local` | Local file system | Development, testing, file-based data processing |
|
|
20
|
+
| `aws-s3` | Amazon S3 storage | Cloud object storage, data lakes, file archives |
|
|
21
|
+
| `aws-redshift` | Amazon Redshift data warehouse | SQL analytics, business intelligence, data warehousing |
|
|
22
|
+
| `postgres` | PostgreSQL server (RDS, Aurora, or self-hosted) | Operational databases, application data, SQL extraction |
|
|
23
|
+
| `delta-share` | Delta Share protocol | Data sharing across organizations, Databricks integration |
|
|
24
|
+
| `http-api` | REST / HTTP API endpoint | External services, SaaS integrations |
|
|
25
|
+
| `presigned-url` | A file reached through a URL signed on demand by an API | Reading from or writing to a bucket Remora holds no credentials for |
|
|
26
|
+
| `aws-sqs` | Amazon SQS queue | Queue-triggered consumer execution (see consumer QUEUE triggers) |
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## **Authentication Methods**
|
|
31
|
+
|
|
32
|
+
The `authentication.method` field selects how Remora connects. The methods available depend on the engine:
|
|
33
|
+
|
|
34
|
+
| Method | Used by | Description |
|
|
35
|
+
| --- | --- | --- |
|
|
36
|
+
| `implicit` | `local` | No credentials — read/write a local path |
|
|
37
|
+
| `access-secret-key` | `aws-s3`, `aws-redshift`, `aws-sqs` | Static AWS access key + secret (optionally a session token) |
|
|
38
|
+
| `iam` | `aws-s3`, `aws-redshift`, `aws-sqs`, `postgres` | Use the AWS SDK default credential chain (EC2/ECS/EKS instance or task role, SSO, env vars) — no static keys stored. On `postgres` this signs a short-lived RDS IAM auth token instead of sending a password |
|
|
39
|
+
| `arn` | `aws-s3`, `aws-redshift`, `aws-sqs`, `postgres` | Assume an IAM role via STS (`roleArn`); base identity comes from the SDK default chain |
|
|
40
|
+
| `bearer-token` | `delta-share`, `http-api`, `presigned-url` | Static bearer token |
|
|
41
|
+
| `oidc` | `delta-share`, `presigned-url` | OAuth2 client-credentials grant (e.g. Okta/Cognito) that mints a short-lived token. This is what machine-to-machine (M2M) authentication on an API Gateway means |
|
|
42
|
+
| `api-key` | `http-api`, `presigned-url` | API key sent in a header |
|
|
43
|
+
| `username-password` | `http-api`, `postgres`, `presigned-url` | HTTP Basic authentication, or a database user and password |
|
|
44
|
+
| `none` | `http-api`, `presigned-url` | No authentication (public endpoints) |
|
|
45
|
+
|
|
46
|
+
> **Security tip:** never hard-code secrets. Reference environment variables with the `{ENV_VAR}` notation (e.g. `"accessKey": "{AWS_ACCESS_KEY_ID}"`), and prefer `iam`/`arn` over static keys on AWS.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## **Local Sources**
|
|
51
|
+
|
|
52
|
+
Local sources connect to files on your local file system or network-accessible directories.
|
|
53
|
+
|
|
54
|
+
### **Configuration Schema**
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"$schema": "https://raw.githubusercontent.com/ForzaLabs/remora-public/refs/heads/main/json_schemas/source-schema.json",
|
|
59
|
+
"name": "source-name",
|
|
60
|
+
"engine": "local",
|
|
61
|
+
"authentication": {
|
|
62
|
+
"method": "implicit",
|
|
63
|
+
"path": "/path/to/data/directory"
|
|
64
|
+
},
|
|
65
|
+
"_version": 1
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### **Authentication Properties**
|
|
70
|
+
|
|
71
|
+
| Property | Required | Description | Example |
|
|
72
|
+
| --- | --- | --- | --- |
|
|
73
|
+
| `method` | ✅ | Must be `"implicit"` | `"implicit"` |
|
|
74
|
+
| `path` | ✅ | Directory path containing data files | `"./data"`, `"/Users/john/datasets"` |
|
|
75
|
+
|
|
76
|
+
### **Supported File Types**
|
|
77
|
+
|
|
78
|
+
Local sources can process:
|
|
79
|
+
|
|
80
|
+
- **JSON** files (`.json`)
|
|
81
|
+
- **JSON Lines** files (`.jsonl`, `.ndjson`)
|
|
82
|
+
- **CSV** files (`.csv`)
|
|
83
|
+
- **Text** files (`.txt`)
|
|
84
|
+
- **Excel** files (`.xlsx`, `.xls`)
|
|
85
|
+
- **XML** files (`.xml`)
|
|
86
|
+
|
|
87
|
+
## Supported compression type
|
|
88
|
+
|
|
89
|
+
Local sources can process:
|
|
90
|
+
|
|
91
|
+
- GZ files (`'GZ'`)
|
|
92
|
+
- TAR files (`'TAR'`)
|
|
93
|
+
- ZIP files (`'ZIP'`)
|
|
94
|
+
|
|
95
|
+
### **Example Configurations**
|
|
96
|
+
|
|
97
|
+
**Development Data Source:**
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"$schema": "https://raw.githubusercontent.com/ForzaLabs/remora-public/refs/heads/main/json_schemas/source-schema.json",
|
|
102
|
+
"name": "local-dev",
|
|
103
|
+
"description": "Local development data files",
|
|
104
|
+
"engine": "local",
|
|
105
|
+
"authentication": {
|
|
106
|
+
"method": "implicit",
|
|
107
|
+
"path": "./demo"
|
|
108
|
+
},
|
|
109
|
+
"_version": 1
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Output Destination:**
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"$schema": "https://raw.githubusercontent.com/ForzaLabs/remora-public/refs/heads/main/json_schemas/source-schema.json",
|
|
118
|
+
"name": "output-folder",
|
|
119
|
+
"description": "Local directory for processed results",
|
|
120
|
+
"engine": "local",
|
|
121
|
+
"authentication": {
|
|
122
|
+
"method": "implicit",
|
|
123
|
+
"path": "./output"
|
|
124
|
+
},
|
|
125
|
+
"_version": 1
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### **Best Practices**
|
|
130
|
+
|
|
131
|
+
- Use **relative paths** for portability across environments
|
|
132
|
+
- Ensure the path exists and has appropriate read/write permissions
|
|
133
|
+
- Use descriptive names that indicate the data purpose
|
|
134
|
+
- Consider file organization within the specified directory
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## **AWS S3 Sources**
|
|
139
|
+
|
|
140
|
+
AWS S3 sources connect to Amazon Simple Storage Service buckets for cloud-based object storage.
|
|
141
|
+
|
|
142
|
+
### **Configuration Schema**
|
|
143
|
+
|
|
144
|
+
```json
|
|
145
|
+
{
|
|
146
|
+
"$schema": "https://raw.githubusercontent.com/ForzaLabs/remora-public/refs/heads/main/json_schemas/source-schema.json",
|
|
147
|
+
"name": "source-name",
|
|
148
|
+
"engine": "aws-s3",
|
|
149
|
+
"authentication": {
|
|
150
|
+
"method": "access-secret-key",
|
|
151
|
+
"region": "aws-region",
|
|
152
|
+
"accessKey": "{AWS_ACCESS_KEY_ID}",
|
|
153
|
+
"secretKey": "{AWS_SECRET_ACCESS_KEY}",
|
|
154
|
+
"sessionToken": "{AWS_SESSION_TOKEN}",
|
|
155
|
+
"bucket": "bucket-name"
|
|
156
|
+
},
|
|
157
|
+
"_version": 1
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### **Authentication Properties**
|
|
162
|
+
|
|
163
|
+
| Property | Required | Description | Example |
|
|
164
|
+
| --- | --- | --- | --- |
|
|
165
|
+
| `method` | ✅ | `"access-secret-key"` for static keys, or `"iam"` / `"arn"` for IAM roles | `"access-secret-key"` |
|
|
166
|
+
| `region` | ✅ | AWS region where the bucket is located | `"us-east-1"`, `"eu-west-1"` |
|
|
167
|
+
| `accessKey` | ✅ | AWS Access Key ID (use environment variables) | `"{AWS_ACCESS_KEY_ID}"` |
|
|
168
|
+
| `secretKey` | ✅ | AWS Secret Access Key (use environment variables) | `"{AWS_SECRET_ACCESS_KEY}"` |
|
|
169
|
+
| `bucket` | ✅ | S3 bucket name | `"my-data-bucket"` |
|
|
170
|
+
| `sessionToken` | ❌ | AWS Session Token for temporary credentials | `"{AWS_SESSION_TOKEN}"` |
|
|
171
|
+
|
|
172
|
+
> `method` can also be `"iam"` or `"arn"` — see [IAM Role Authentication](#iam-role-authentication) below. With those, `accessKey`/`secretKey` are not required.
|
|
173
|
+
|
|
174
|
+
### **IAM Role Authentication**
|
|
175
|
+
|
|
176
|
+
Instead of static keys, AWS sources (`aws-s3`, `aws-redshift`, `aws-sqs`) can authenticate through IAM roles — the recommended approach for anything running on AWS:
|
|
177
|
+
|
|
178
|
+
- **`method: "iam"`** — use the AWS SDK default credential chain (EC2/ECS/EKS instance or task role, SSO, or `AWS_*` env vars). No keys are stored in the config.
|
|
179
|
+
|
|
180
|
+
```json
|
|
181
|
+
{
|
|
182
|
+
"authentication": {
|
|
183
|
+
"method": "iam",
|
|
184
|
+
"region": "us-east-1",
|
|
185
|
+
"bucket": "company-data-lake"
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
- **`method: "arn"`** — assume a specific IAM role via STS. The base identity that performs the AssumeRole call still comes from the SDK default chain (no data-access keys in the config). Useful for cross-account access.
|
|
191
|
+
|
|
192
|
+
| Property | Required | Description |
|
|
193
|
+
| --- | --- | --- |
|
|
194
|
+
| `roleArn` | ✅ | ARN of the role to assume, e.g. `arn:aws:iam::123456789012:role/remora-reader` |
|
|
195
|
+
| `roleSessionName` | ❌ | Session name for the assumed credentials (defaults to `remora`) |
|
|
196
|
+
| `externalId` | ❌ | STS ExternalId for third-party / cross-account trust policies |
|
|
197
|
+
|
|
198
|
+
```json
|
|
199
|
+
{
|
|
200
|
+
"authentication": {
|
|
201
|
+
"method": "arn",
|
|
202
|
+
"region": "eu-west-1",
|
|
203
|
+
"bucket": "analytics-exports",
|
|
204
|
+
"roleArn": "arn:aws:iam::123456789012:role/remora-s3-reader",
|
|
205
|
+
"externalId": "{REMORA_STS_EXTERNAL_ID}"
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### **Security Best Practices**
|
|
211
|
+
|
|
212
|
+
**Environment Variables (Recommended):**
|
|
213
|
+
|
|
214
|
+
```json
|
|
215
|
+
{
|
|
216
|
+
"authentication": {
|
|
217
|
+
"method": "access-secret-key",
|
|
218
|
+
"region": "us-east-1",
|
|
219
|
+
"accessKey": "{AWS_ACCESS_KEY_ID}",
|
|
220
|
+
"secretKey": "{AWS_SECRET_ACCESS_KEY}",
|
|
221
|
+
"sessionToken": "{AWS_SESSION_TOKEN}",
|
|
222
|
+
"bucket": "my-data-bucket"
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Set these environment variables:
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
export AWS_ACCESS_KEY_ID=your_access_key_here
|
|
231
|
+
export AWS_SECRET_ACCESS_KEY=your_secret_key_here
|
|
232
|
+
export AWS_SESSION_TOKEN=your_session_token_here# Optional
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### **IAM Permissions Required**
|
|
236
|
+
|
|
237
|
+
Your AWS credentials need the following S3 permissions:
|
|
238
|
+
|
|
239
|
+
```json
|
|
240
|
+
{
|
|
241
|
+
"Version": "2012-10-17",
|
|
242
|
+
"Statement": [
|
|
243
|
+
{
|
|
244
|
+
"Effect": "Allow",
|
|
245
|
+
"Action": [
|
|
246
|
+
"s3:GetObject",
|
|
247
|
+
"s3:ListBucket",
|
|
248
|
+
"s3:PutObject",
|
|
249
|
+
"s3:DeleteObject"
|
|
250
|
+
],
|
|
251
|
+
"Resource": [
|
|
252
|
+
"arn:aws:s3:::your-bucket-name",
|
|
253
|
+
"arn:aws:s3:::your-bucket-name/*"
|
|
254
|
+
]
|
|
255
|
+
}
|
|
256
|
+
]
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### **Example Configurations**
|
|
261
|
+
|
|
262
|
+
**Data Lake Source:**
|
|
263
|
+
|
|
264
|
+
```json
|
|
265
|
+
{
|
|
266
|
+
"$schema": "https://raw.githubusercontent.com/ForzaLabs/remora-public/refs/heads/main/json_schemas/source-schema.json",
|
|
267
|
+
"name": "data-lake",
|
|
268
|
+
"description": "Production data lake in S3",
|
|
269
|
+
"engine": "aws-s3",
|
|
270
|
+
"authentication": {
|
|
271
|
+
"method": "access-secret-key",
|
|
272
|
+
"region": "us-east-1",
|
|
273
|
+
"accessKey": "{AWS_ACCESS_KEY_ID}",
|
|
274
|
+
"secretKey": "{AWS_SECRET_ACCESS_KEY}",
|
|
275
|
+
"bucket": "company-data-lake"
|
|
276
|
+
},
|
|
277
|
+
"_version": 1
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**Archive Storage:**
|
|
282
|
+
|
|
283
|
+
```json
|
|
284
|
+
{
|
|
285
|
+
"$schema": "https://raw.githubusercontent.com/ForzaLabs/remora-public/refs/heads/main/json_schemas/source-schema.json",
|
|
286
|
+
"name": "archive-storage",
|
|
287
|
+
"description": "Historical data archive",
|
|
288
|
+
"engine": "aws-s3",
|
|
289
|
+
"authentication": {
|
|
290
|
+
"method": "access-secret-key",
|
|
291
|
+
"region": "us-west-2",
|
|
292
|
+
"accessKey": "{ARCHIVE_ACCESS_KEY}",
|
|
293
|
+
"secretKey": "{ARCHIVE_SECRET_KEY}",
|
|
294
|
+
"bucket": "historical-data-archive"
|
|
295
|
+
},
|
|
296
|
+
"_version": 1
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### **Supported Operations**
|
|
301
|
+
|
|
302
|
+
- **Reading files** from S3 buckets
|
|
303
|
+
- **Writing processed results** back to S3
|
|
304
|
+
- **Listing objects** within bucket prefixes
|
|
305
|
+
- **Streaming large files** for memory-efficient processing
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## **AWS Redshift Sources**
|
|
310
|
+
|
|
311
|
+
AWS Redshift sources connect to Amazon Redshift data warehouses for SQL-based analytics and data processing.
|
|
312
|
+
|
|
313
|
+
### **Configuration Schema**
|
|
314
|
+
|
|
315
|
+
```json
|
|
316
|
+
{
|
|
317
|
+
"$schema": "https://raw.githubusercontent.com/ForzaLabs/remora-public/refs/heads/main/json_schemas/source-schema.json",
|
|
318
|
+
"name": "source-name",
|
|
319
|
+
"engine": "aws-redshift",
|
|
320
|
+
"authentication": {
|
|
321
|
+
"method": "access-secret-key",
|
|
322
|
+
"region": "aws-region",
|
|
323
|
+
"accessKey": "{AWS_ACCESS_KEY_ID}",
|
|
324
|
+
"secretKey": "{AWS_SECRET_ACCESS_KEY}",
|
|
325
|
+
"sessionToken": "{AWS_SESSION_TOKEN}",
|
|
326
|
+
"database": "database-name",
|
|
327
|
+
"workgroup": "workgroup-name"
|
|
328
|
+
},
|
|
329
|
+
"_version": 1
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### **Authentication Properties**
|
|
334
|
+
|
|
335
|
+
| Property | Required | Description | Example |
|
|
336
|
+
| --- | --- | --- | --- |
|
|
337
|
+
| `method` | ✅ | `"access-secret-key"` for static keys, or `"iam"` / `"arn"` for IAM roles | `"access-secret-key"` |
|
|
338
|
+
| `region` | ✅ | AWS region where Redshift cluster is located | `"us-east-1"`, `"eu-west-1"` |
|
|
339
|
+
| `accessKey` | ✅ | AWS Access Key ID (use environment variables) | `"{AWS_ACCESS_KEY_ID}"` |
|
|
340
|
+
| `secretKey` | ✅ | AWS Secret Access Key (use environment variables) | `"{AWS_SECRET_ACCESS_KEY}"` |
|
|
341
|
+
| `database` | ✅ | Redshift database name | `"analytics_db"`, `"data_warehouse"` |
|
|
342
|
+
| `workgroup` | ✅ | Redshift workgroup name | `"primary"`, `"analytics-workgroup"` |
|
|
343
|
+
| `sessionToken` | ❌ | AWS Session Token for temporary credentials | `"{AWS_SESSION_TOKEN}"` |
|
|
344
|
+
|
|
345
|
+
### **IAM Permissions Required**
|
|
346
|
+
|
|
347
|
+
Your AWS credentials need the following Redshift permissions:
|
|
348
|
+
|
|
349
|
+
```json
|
|
350
|
+
{
|
|
351
|
+
"Version": "2012-10-17",
|
|
352
|
+
"Statement": [
|
|
353
|
+
{
|
|
354
|
+
"Effect": "Allow",
|
|
355
|
+
"Action": [
|
|
356
|
+
"redshift-data:ExecuteStatement",
|
|
357
|
+
"redshift-data:DescribeStatement",
|
|
358
|
+
"redshift-data:GetStatementResult",
|
|
359
|
+
"redshift-data:ListStatements"
|
|
360
|
+
],
|
|
361
|
+
"Resource": "*"
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
"Effect": "Allow",
|
|
365
|
+
"Action": [
|
|
366
|
+
"redshift-serverless:GetWorkgroup",
|
|
367
|
+
"redshift-serverless:GetNamespace"
|
|
368
|
+
],
|
|
369
|
+
"Resource": "*"
|
|
370
|
+
}
|
|
371
|
+
]
|
|
372
|
+
}
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### **Example Configurations**
|
|
376
|
+
|
|
377
|
+
**Analytics Data Warehouse:**
|
|
378
|
+
|
|
379
|
+
```json
|
|
380
|
+
{
|
|
381
|
+
"$schema": "https://raw.githubusercontent.com/ForzaLabs/remora-public/refs/heads/main/json_schemas/source-schema.json",
|
|
382
|
+
"name": "analytics-warehouse",
|
|
383
|
+
"description": "Main analytics data warehouse",
|
|
384
|
+
"engine": "aws-redshift",
|
|
385
|
+
"authentication": {
|
|
386
|
+
"method": "access-secret-key",
|
|
387
|
+
"region": "us-east-1",
|
|
388
|
+
"accessKey": "{REDSHIFT_ACCESS_KEY}",
|
|
389
|
+
"secretKey": "{REDSHIFT_SECRET_KEY}",
|
|
390
|
+
"database": "analytics_db",
|
|
391
|
+
"workgroup": "analytics-workgroup"
|
|
392
|
+
},
|
|
393
|
+
"_version": 1
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
**Development Environment:**
|
|
398
|
+
|
|
399
|
+
```json
|
|
400
|
+
{
|
|
401
|
+
"$schema": "https://raw.githubusercontent.com/ForzaLabs/remora-public/refs/heads/main/json_schemas/source-schema.json",
|
|
402
|
+
"name": "dev-redshift",
|
|
403
|
+
"description": "Development Redshift environment",
|
|
404
|
+
"engine": "aws-redshift",
|
|
405
|
+
"authentication": {
|
|
406
|
+
"method": "access-secret-key",
|
|
407
|
+
"region": "us-west-2",
|
|
408
|
+
"accessKey": "{DEV_ACCESS_KEY}",
|
|
409
|
+
"secretKey": "{DEV_SECRET_KEY}",
|
|
410
|
+
"database": "dev_database",
|
|
411
|
+
"workgroup": "dev-workgroup"
|
|
412
|
+
},
|
|
413
|
+
"_version": 1
|
|
414
|
+
}
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### **How a run reads a table**
|
|
418
|
+
|
|
419
|
+
A producer on a Redshift source names its table with `sqlTable` and lists the columns it wants as
|
|
420
|
+
dimensions — there is no `fileKey` and no `fileType`. Remora builds the `SELECT` from those
|
|
421
|
+
dimensions (a dimension's `alias` is the column name when it differs from the dimension name), runs
|
|
422
|
+
it through the Redshift Data API, and stages the result as a local dataset file that the rest of the
|
|
423
|
+
pipeline reads. The staging read follows the Data API's `NextToken` pages to the end of the result
|
|
424
|
+
and compares what it staged against the row count the cluster reports, so a result larger than one
|
|
425
|
+
page can never come back short without an error.
|
|
426
|
+
|
|
427
|
+
```json
|
|
428
|
+
{
|
|
429
|
+
"name": "p_customers",
|
|
430
|
+
"source": "analytics-warehouse",
|
|
431
|
+
"settings": { "sqlTable": "analytics.customers" },
|
|
432
|
+
"dimensions": [
|
|
433
|
+
{ "name": "id", "type": "string", "pk": true },
|
|
434
|
+
{ "name": "firstName", "type": "string", "alias": "first_name" },
|
|
435
|
+
{ "name": "monthlySpend", "type": "number", "alias": "monthly_spend" }
|
|
436
|
+
],
|
|
437
|
+
"_version": 1
|
|
438
|
+
}
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
`sqlTable` may be `table` or `schema.table`. An unqualified name falls back to the source's `schema`.
|
|
442
|
+
|
|
443
|
+
### **SQL Query Limitations**
|
|
444
|
+
|
|
445
|
+
- **Maximum query rows**: `SQL_MAX_QUERY_ROWS` (default `10000`) caps the ad-hoc reads behind
|
|
446
|
+
`remora sample` and `remora discover`, which return rows in memory. It does **not** cap a run: a run
|
|
447
|
+
stages the whole table a page at a time, bounded by disk rather than by this setting
|
|
448
|
+
- **Statement timeout**: a staging statement is polled for up to 30 minutes before it is abandoned
|
|
449
|
+
- **Concurrent queries**: Limited by Redshift workgroup configuration
|
|
450
|
+
- **Memory usage**: no page of the result is held once it has been written to the staged file
|
|
451
|
+
|
|
452
|
+
### **Performance Considerations**
|
|
453
|
+
|
|
454
|
+
- **Index usage** in underlying tables improves query performance
|
|
455
|
+
- **Workgroup sizing** affects concurrent query capacity
|
|
456
|
+
- Producers query the source directly; use the producer-level `direct: true` setting to signal read-only access (see the Producers guide)
|
|
457
|
+
|
|
458
|
+
> **IAM authentication:** Redshift sources can also use `method: "iam"` (or `"arn"`). In that case supply `host`, `clusterId`, `port`, and an `iamProfile` (or `roleArn` for `arn`) instead of static keys.
|
|
459
|
+
|
|
460
|
+
---
|
|
461
|
+
|
|
462
|
+
## **PostgreSQL Sources**
|
|
463
|
+
|
|
464
|
+
`postgres` sources read from a PostgreSQL server over the native wire protocol — Amazon RDS, Aurora
|
|
465
|
+
PostgreSQL, or a self-hosted instance. The engine is not named `aws-postgres` because nothing about
|
|
466
|
+
it requires AWS; the IAM auth token below is the only AWS-specific part, and it is optional.
|
|
467
|
+
|
|
468
|
+
Unlike Redshift, which is reached over an HTTPS API, a `postgres` source needs network reachability
|
|
469
|
+
to the database: a security group that admits the machine running remora, and a route to it (a VPC,
|
|
470
|
+
a peering connection or a VPN) when the database is not public.
|
|
471
|
+
|
|
472
|
+
### **Configuration Schema**
|
|
473
|
+
|
|
474
|
+
```json
|
|
475
|
+
{
|
|
476
|
+
"$schema": "https://raw.githubusercontent.com/ForzaLabs/remora-public/refs/heads/main/json_schemas/source-schema.json",
|
|
477
|
+
"name": "source-name",
|
|
478
|
+
"engine": "postgres",
|
|
479
|
+
"authentication": {
|
|
480
|
+
"method": "username-password",
|
|
481
|
+
"host": "{PG_HOST}",
|
|
482
|
+
"port": "5432",
|
|
483
|
+
"database": "analytics",
|
|
484
|
+
"schema": "public",
|
|
485
|
+
"user": "{PG_USER}",
|
|
486
|
+
"password": "{PG_PASSWORD}",
|
|
487
|
+
"sslMode": "require"
|
|
488
|
+
},
|
|
489
|
+
"_version": 1
|
|
490
|
+
}
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
### **Authentication Properties**
|
|
494
|
+
|
|
495
|
+
| Property | Required | Description | Example |
|
|
496
|
+
| --- | --- | --- | --- |
|
|
497
|
+
| `method` | ✅ | `"username-password"` for a password, or `"iam"` / `"arn"` for an RDS IAM auth token | `"username-password"` |
|
|
498
|
+
| `host` | ✅ | Hostname of the server or cluster endpoint | `"db.cluster-abc.eu-west-1.rds.amazonaws.com"` |
|
|
499
|
+
| `database` | ✅ | Database to connect to | `"analytics"` |
|
|
500
|
+
| `user` | ✅ | Database user | `"remora_reader"` |
|
|
501
|
+
| `password` | ⚠️ | Required unless the method signs an IAM token. Use the `{ENV_VAR}` notation | `"{PG_PASSWORD}"` |
|
|
502
|
+
| `region` | ⚠️ | Required when the method is `"iam"` or `"arn"`, to sign the token | `"eu-west-1"` |
|
|
503
|
+
| `roleArn` | ⚠️ | Required when the method is `"arn"` | `"arn:aws:iam::123456789012:role/remora-reader"` |
|
|
504
|
+
| `port` | ❌ | Defaults to `5432` | `"5432"` |
|
|
505
|
+
| `schema` | ❌ | Default schema for producers whose `sqlTable` is unqualified | `"public"` |
|
|
506
|
+
| `sslMode` | ❌ | `disable`, `require` (default for a remote host) or `verify-full` | `"require"` |
|
|
507
|
+
| `connectionTimeoutMs` | ❌ | Connection and handshake timeout in milliseconds, default `30000` | `10000` |
|
|
508
|
+
|
|
509
|
+
⚠️ = required depending on `method`. `remora verify` reports which of these is missing for the method the source declares.
|
|
510
|
+
|
|
511
|
+
### **TLS**
|
|
512
|
+
|
|
513
|
+
`sslMode` defaults to `require` for a remote host and `disable` for `localhost` / `127.0.0.1`, since
|
|
514
|
+
a database on the same machine usually speaks no TLS at all.
|
|
515
|
+
|
|
516
|
+
- `require` encrypts the connection but does not verify the server certificate. This is what RDS
|
|
517
|
+
accepts with nothing else configured
|
|
518
|
+
- `verify-full` also verifies the certificate chain against the process trust store. Point
|
|
519
|
+
`NODE_EXTRA_CA_CERTS` at the RDS CA bundle when you use it, or the handshake will fail
|
|
520
|
+
|
|
521
|
+
### **IAM authentication (RDS and Aurora)**
|
|
522
|
+
|
|
523
|
+
With `method: "iam"` there is no database password anywhere in the configuration: remora signs a
|
|
524
|
+
short-lived RDS auth token for each connection and sends that instead. The AWS credentials that sign
|
|
525
|
+
it come from the SDK default credential chain — an ECS task role, an EC2 instance role, SSO, or
|
|
526
|
+
environment variables — exactly as for every other AWS engine. Use `method: "arn"` with a `roleArn`
|
|
527
|
+
to sign it with an assumed role instead.
|
|
528
|
+
|
|
529
|
+
This requires IAM authentication to be enabled on the instance or cluster, a database user granted
|
|
530
|
+
`rds_iam`, and the following permission:
|
|
531
|
+
|
|
532
|
+
```json
|
|
533
|
+
{
|
|
534
|
+
"Version": "2012-10-17",
|
|
535
|
+
"Statement": [
|
|
536
|
+
{
|
|
537
|
+
"Effect": "Allow",
|
|
538
|
+
"Action": ["rds-db:connect"],
|
|
539
|
+
"Resource": "arn:aws:rds-db:eu-west-1:123456789012:dbuser:cluster-ABCDEFGH/remora_reader"
|
|
540
|
+
}
|
|
541
|
+
]
|
|
542
|
+
}
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
### **Example — Aurora with IAM authentication**
|
|
546
|
+
|
|
547
|
+
```json
|
|
548
|
+
{
|
|
549
|
+
"$schema": "https://raw.githubusercontent.com/ForzaLabs/remora-public/refs/heads/main/json_schemas/source-schema.json",
|
|
550
|
+
"name": "aurora-analytics",
|
|
551
|
+
"description": "Aurora PostgreSQL read replica, no stored password",
|
|
552
|
+
"engine": "postgres",
|
|
553
|
+
"authentication": {
|
|
554
|
+
"method": "iam",
|
|
555
|
+
"host": "analytics.cluster-ro-abc123.eu-west-1.rds.amazonaws.com",
|
|
556
|
+
"port": "5432",
|
|
557
|
+
"database": "analytics",
|
|
558
|
+
"schema": "public",
|
|
559
|
+
"user": "remora_reader",
|
|
560
|
+
"region": "eu-west-1",
|
|
561
|
+
"sslMode": "require"
|
|
562
|
+
},
|
|
563
|
+
"_version": 1
|
|
564
|
+
}
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
### **How a run reads a table**
|
|
568
|
+
|
|
569
|
+
Exactly as for Redshift: the producer names its table with `sqlTable` and its columns as dimensions,
|
|
570
|
+
and remora builds the `SELECT` and stages the result as a local dataset file. The read goes through a
|
|
571
|
+
server-side cursor inside a `READ ONLY` transaction, pulling 10 000 rows per round trip and writing
|
|
572
|
+
each batch to disk before asking for the next, so staging a large table costs disk and not memory.
|
|
573
|
+
|
|
574
|
+
### **Performance Considerations**
|
|
575
|
+
|
|
576
|
+
- The staging query is a plain `SELECT` of the producer's columns. Narrow the producer's dimensions
|
|
577
|
+
rather than selecting columns the consumer discards
|
|
578
|
+
- Point the source at a **read replica** where one exists: a full-table read competes with the
|
|
579
|
+
primary's operational traffic
|
|
580
|
+
- Filters and limits are not yet pushed into the `WHERE` / `LIMIT` of the staging query, so the whole
|
|
581
|
+
table is read and narrowed by the pipeline
|
|
582
|
+
|
|
583
|
+
---
|
|
584
|
+
|
|
585
|
+
## **Delta Share Sources**
|
|
586
|
+
|
|
587
|
+
Delta Share sources connect to Delta Share endpoints for secure data sharing across organizations and platforms.
|
|
588
|
+
|
|
589
|
+
### **Configuration Schema**
|
|
590
|
+
|
|
591
|
+
```json
|
|
592
|
+
{
|
|
593
|
+
"$schema": "https://raw.githubusercontent.com/ForzaLabs/remora-public/refs/heads/main/json_schemas/source-schema.json",
|
|
594
|
+
"name": "source-name",
|
|
595
|
+
"engine": "delta-share",
|
|
596
|
+
"authentication": {
|
|
597
|
+
"method": "bearer-token",
|
|
598
|
+
"host": "https://sharing-endpoint.com/api/2.0/delta-sharing/metastores/...",
|
|
599
|
+
"bearerToken": "{DELTA_TOKEN}",
|
|
600
|
+
"share": "share-name",
|
|
601
|
+
"schema": "schema-name",
|
|
602
|
+
"table": "table-name"
|
|
603
|
+
},
|
|
604
|
+
"_version": 1
|
|
605
|
+
}
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
### **Authentication Properties**
|
|
609
|
+
|
|
610
|
+
| Property | Required | Description | Example |
|
|
611
|
+
| --- | --- | --- | --- |
|
|
612
|
+
| `method` | ✅ | `"bearer-token"` for a static token, or `"oidc"` for OAuth2 client-credentials | `"bearer-token"` |
|
|
613
|
+
| `host` | ✅ | Delta Share endpoint URL | `"https://oregon.cloud.databricks.com/api/2.0/delta-sharing/metastores/..."` |
|
|
614
|
+
| `bearerToken` | ✅ | Delta Share authentication token | `"{DELTA_TOKEN}"` |
|
|
615
|
+
| `share` | ✅ | Share name in Delta Share | `"analytics_share"`, `"customer_data"` |
|
|
616
|
+
| `schema` | ✅ | Schema within the share | `"default"`, `"production"` |
|
|
617
|
+
| `table` | ✅ | Table name within the schema | `"customer_orders"`, `"product_catalog"` |
|
|
618
|
+
|
|
619
|
+
### **OIDC Authentication**
|
|
620
|
+
|
|
621
|
+
Instead of a static `bearerToken`, Delta Share sources can use `method: "oidc"` to obtain a short-lived token via the OAuth2 client-credentials grant (e.g. Okta or Cognito). Remora fetches and refreshes the token automatically.
|
|
622
|
+
|
|
623
|
+
| Property | Required | Description |
|
|
624
|
+
| --- | --- | --- |
|
|
625
|
+
| `tokenEndpoint` | ✅ | OAuth2 token endpoint that issues the access token |
|
|
626
|
+
| `clientId` | ✅ | OAuth2 client identifier |
|
|
627
|
+
| `clientSecret` | ✅ | OAuth2 client secret |
|
|
628
|
+
| `scope` | ❌ | Space-delimited scopes (required by Okta/Cognito for client-credentials) |
|
|
629
|
+
| `audience` | ❌ | Audience/resource parameter for IdPs that require it |
|
|
630
|
+
| `clientAuthMethod` | ❌ | How client credentials are sent: `"basic"` (default) or `"post"` (request body) |
|
|
631
|
+
|
|
632
|
+
```json
|
|
633
|
+
{
|
|
634
|
+
"engine": "delta-share",
|
|
635
|
+
"authentication": {
|
|
636
|
+
"method": "oidc",
|
|
637
|
+
"host": "https://sharing.example.com/delta-sharing",
|
|
638
|
+
"share": "my_share",
|
|
639
|
+
"schema": "my_schema",
|
|
640
|
+
"table": "my_table",
|
|
641
|
+
"tokenEndpoint": "https://example.okta.com/oauth2/default/v1/token",
|
|
642
|
+
"clientId": "{DELTA_SHARE_CLIENT_ID}",
|
|
643
|
+
"clientSecret": "{DELTA_SHARE_CLIENT_SECRET}",
|
|
644
|
+
"scope": "delta_sharing"
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
### **Security Configuration**
|
|
651
|
+
|
|
652
|
+
**Environment Variables (Recommended):**
|
|
653
|
+
|
|
654
|
+
```json
|
|
655
|
+
{
|
|
656
|
+
"authentication": {
|
|
657
|
+
"method": "bearer-token",
|
|
658
|
+
"host": "https://oregon.cloud.databricks.com/api/2.0/delta-sharing/metastores/f53bde37-1e8b-4668-a1e4-e62333110018",
|
|
659
|
+
"bearerToken": "{DELTA_TOKEN}",
|
|
660
|
+
"share": "analytics_share",
|
|
661
|
+
"schema": "default",
|
|
662
|
+
"table": "customer_data"
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
Set the environment variable:
|
|
668
|
+
|
|
669
|
+
```bash
|
|
670
|
+
export DELTA_TOKEN=your_delta_share_token_here
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
### **Example Configurations**
|
|
674
|
+
|
|
675
|
+
**External Data Share:**
|
|
676
|
+
|
|
677
|
+
```json
|
|
678
|
+
{
|
|
679
|
+
"$schema": "https://raw.githubusercontent.com/ForzaLabs/remora-public/refs/heads/main/json_schemas/source-schema.json",
|
|
680
|
+
"name": "partner-data",
|
|
681
|
+
"description": "Shared data from business partner",
|
|
682
|
+
"engine": "delta-share",
|
|
683
|
+
"authentication": {
|
|
684
|
+
"method": "bearer-token",
|
|
685
|
+
"host": "https://partner.cloud.databricks.com/api/2.0/delta-sharing/metastores/abc123-def4-5678-90ab-cdef12345678",
|
|
686
|
+
"bearerToken": "{PARTNER_DELTA_TOKEN}",
|
|
687
|
+
"share": "customer_insights",
|
|
688
|
+
"schema": "public",
|
|
689
|
+
"table": "demographic_data"
|
|
690
|
+
},
|
|
691
|
+
"_version": 1
|
|
692
|
+
}
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
**Internal Data Sharing:**
|
|
696
|
+
|
|
697
|
+
```json
|
|
698
|
+
{
|
|
699
|
+
"$schema": "https://raw.githubusercontent.com/ForzaLabs/remora-public/refs/heads/main/json_schemas/source-schema.json",
|
|
700
|
+
"name": "internal-analytics",
|
|
701
|
+
"description": "Internal analytics data via Delta Share",
|
|
702
|
+
"engine": "delta-share",
|
|
703
|
+
"authentication": {
|
|
704
|
+
"method": "bearer-token",
|
|
705
|
+
"host": "https://oregon.cloud.databricks.com/api/2.0/delta-sharing/metastores/internal-metastore-id",
|
|
706
|
+
"bearerToken": "{INTERNAL_DELTA_TOKEN}",
|
|
707
|
+
"share": "company_analytics",
|
|
708
|
+
"schema": "processed",
|
|
709
|
+
"table": "quarterly_metrics"
|
|
710
|
+
},
|
|
711
|
+
"_version": 1
|
|
712
|
+
}
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
### **Delta Share Features**
|
|
716
|
+
|
|
717
|
+
- **Version control**: Access to historical versions of shared data
|
|
718
|
+
- **Incremental updates**: Efficient processing of data changes
|
|
719
|
+
- **Schema evolution**: Automatic handling of schema changes
|
|
720
|
+
- **Access control**: Token-based security with fine-grained permissions
|
|
721
|
+
|
|
722
|
+
---
|
|
723
|
+
|
|
724
|
+
## **HTTP API Sources**
|
|
725
|
+
|
|
726
|
+
HTTP API sources read data from a REST endpoint. The source holds the base `url` and connection settings; a producer using this source sets `fileKey` to the endpoint path and `fileType` to the response format (see the Producers guide).
|
|
727
|
+
|
|
728
|
+
### **Configuration Schema**
|
|
729
|
+
|
|
730
|
+
```json
|
|
731
|
+
{
|
|
732
|
+
"$schema": "https://raw.githubusercontent.com/ForzaLabs/remora-public/refs/heads/main/json_schemas/source-schema.json",
|
|
733
|
+
"name": "rest-api",
|
|
734
|
+
"engine": "http-api",
|
|
735
|
+
"authentication": {
|
|
736
|
+
"method": "bearer-token",
|
|
737
|
+
"url": "https://api.example.com",
|
|
738
|
+
"bearerToken": "{API_BEARER_TOKEN}",
|
|
739
|
+
"headers": { "Accept": "application/json" },
|
|
740
|
+
"timeout": 30000
|
|
741
|
+
},
|
|
742
|
+
"_version": 1
|
|
743
|
+
}
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
### **Authentication Properties**
|
|
747
|
+
|
|
748
|
+
| Property | Required | Description | Example |
|
|
749
|
+
| --- | --- | --- | --- |
|
|
750
|
+
| `method` | ✅ | `"none"`, `"api-key"`, `"bearer-token"`, or `"username-password"` (HTTP Basic) | `"bearer-token"` |
|
|
751
|
+
| `url` | ✅ | Base URL of the API | `"https://api.example.com"` |
|
|
752
|
+
| `headers` | ❌ | Custom headers sent with every request | `{ "Accept": "application/json" }` |
|
|
753
|
+
| `queryParams` | ❌ | Default query parameters sent with every request | `{ "version": "2" }` |
|
|
754
|
+
| `httpMethod` | ❌ | HTTP method (default `GET`) | `"GET"`, `"POST"` |
|
|
755
|
+
| `timeout` | ❌ | Request timeout in milliseconds (default `30000`) | `30000` |
|
|
756
|
+
| `bearerToken` | ❌ | Token for `bearer-token` | `"{API_BEARER_TOKEN}"` |
|
|
757
|
+
| `apiKey` | ❌ | Key for `api-key` | `"{API_KEY}"` |
|
|
758
|
+
| `apiKeyHeader` | ❌ | Header name for the API key (default `X-API-Key`) | `"X-API-Key"` |
|
|
759
|
+
| `user` / `password` | ❌ | Credentials for `username-password` (HTTP Basic) | `"{API_USER}"` / `"{API_PASSWORD}"` |
|
|
760
|
+
|
|
761
|
+
### **Example — API-key authentication**
|
|
762
|
+
|
|
763
|
+
```json
|
|
764
|
+
{
|
|
765
|
+
"engine": "http-api",
|
|
766
|
+
"authentication": {
|
|
767
|
+
"method": "api-key",
|
|
768
|
+
"url": "https://api.example.com",
|
|
769
|
+
"apiKey": "{SERVICE_API_KEY}",
|
|
770
|
+
"apiKeyHeader": "X-API-Key",
|
|
771
|
+
"headers": { "Accept": "application/json" }
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
### **Example — Public API (no auth)**
|
|
777
|
+
|
|
778
|
+
```json
|
|
779
|
+
{
|
|
780
|
+
"engine": "http-api",
|
|
781
|
+
"authentication": {
|
|
782
|
+
"method": "none",
|
|
783
|
+
"url": "https://api.publicapis.org",
|
|
784
|
+
"httpMethod": "GET"
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
---
|
|
790
|
+
|
|
791
|
+
## **Presigned URL Sources**
|
|
792
|
+
|
|
793
|
+
Use this engine when the bucket is reachable only through a URL that something else signs: an API — typically a Lambda behind API Gateway — mints a presigned URL for one file, and Remora transfers against it. Remora needs no AWS credentials at all, because a signed URL authenticates itself.
|
|
794
|
+
|
|
795
|
+
It works in both directions. Referenced by a producer it **downloads**; referenced as a consumer output's `exportDestination` it **uploads**.
|
|
796
|
+
|
|
797
|
+
### **How it works**
|
|
798
|
+
|
|
799
|
+
Every transfer is two calls:
|
|
800
|
+
|
|
801
|
+
1. **Mint** — Remora calls your endpoint with the file name and reads the signed URL out of the JSON response.
|
|
802
|
+
2. **Transfer** — Remora GETs or PUTs that URL directly. No credentials are sent; the signature in the query string is the authentication.
|
|
803
|
+
|
|
804
|
+
The URL is minted immediately before each transfer, per file. That is deliberate: a URL in a config file would expire, and would address only one object — but a consumer that splits its output (`split.maxFileSizeGb`, or `split.by`) writes several files, and each gets its own URL.
|
|
805
|
+
|
|
806
|
+
### **Configuration Schema**
|
|
807
|
+
|
|
808
|
+
```json
|
|
809
|
+
{
|
|
810
|
+
"name": "partner_upload",
|
|
811
|
+
"engine": "presigned-url",
|
|
812
|
+
"authentication": {
|
|
813
|
+
"method": "oidc",
|
|
814
|
+
"url": "https://api.example.com/v1/uploads/presign",
|
|
815
|
+
"httpMethod": "POST",
|
|
816
|
+
"fileNameParam": "filename",
|
|
817
|
+
"signedUrlField": "uploadUrl",
|
|
818
|
+
"tokenEndpoint": "https://example.auth.eu-west-1.amazoncognito.com/oauth2/token",
|
|
819
|
+
"clientId": "{PRESIGN_CLIENT_ID}",
|
|
820
|
+
"clientSecret": "{PRESIGN_CLIENT_SECRET}",
|
|
821
|
+
"scope": "uploads/write"
|
|
822
|
+
},
|
|
823
|
+
"_version": 1
|
|
824
|
+
}
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
### **Authentication Properties**
|
|
828
|
+
|
|
829
|
+
Everything here configures the **mint call only**. The signed URL that comes back is transferred unauthenticated.
|
|
830
|
+
|
|
831
|
+
| Property | Required | Description |
|
|
832
|
+
| --- | --- | --- |
|
|
833
|
+
| `method` | Yes | How to authenticate to the minting endpoint: `oidc`, `bearer-token`, `api-key`, `username-password` or `none` |
|
|
834
|
+
| `url` | Yes | The minting endpoint |
|
|
835
|
+
| `httpMethod` | No | Method for the mint call. Defaults to `POST` |
|
|
836
|
+
| `fileNameParam` | No | Field carrying the file name — in the JSON body on `POST`/`PUT`/`PATCH`, as a query parameter otherwise. Defaults to `filename` |
|
|
837
|
+
| `signedUrlField` | No | Dot path to the URL in the response, e.g. `data.uploadUrl`. Defaults to `url` |
|
|
838
|
+
| `signedHeadersField` | No | Dot path to an object of headers in the response. Set it when your minter signs headers (see below) |
|
|
839
|
+
| `signedUrlMethod` | No | Method used against the signed URL. Defaults to `GET` when reading and `PUT` when exporting |
|
|
840
|
+
| `headers`, `queryParams`, `timeout` | No | Sent with the mint call, as for `http-api` |
|
|
841
|
+
|
|
842
|
+
An M2M-protected API Gateway is the `oidc` method: set `tokenEndpoint`, `clientId`, `clientSecret` and `scope`, and the token is cached and refreshed shortly before it expires rather than fetched per file.
|
|
843
|
+
|
|
844
|
+
### **What your endpoint must return**
|
|
845
|
+
|
|
846
|
+
A JSON response holding the URL. Anything else in it is ignored:
|
|
847
|
+
|
|
848
|
+
```json
|
|
849
|
+
{ "uploadUrl": "https://bucket.s3.eu-west-1.amazonaws.com/exports/report.csv?X-Amz-Signature=..." }
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
If the signature covers headers — a `Content-Type`, any `x-amz-*` — return them too and point `signedHeadersField` at them, because they must be sent back verbatim or S3 answers `403 SignatureDoesNotMatch`:
|
|
853
|
+
|
|
854
|
+
```json
|
|
855
|
+
{ "uploadUrl": "https://...", "headers": { "content-type": "text/csv" } }
|
|
856
|
+
```
|
|
857
|
+
|
|
858
|
+
### **Example — reading a file**
|
|
859
|
+
|
|
860
|
+
```json
|
|
861
|
+
{
|
|
862
|
+
"name": "partner_download",
|
|
863
|
+
"engine": "presigned-url",
|
|
864
|
+
"authentication": {
|
|
865
|
+
"method": "api-key",
|
|
866
|
+
"url": "https://api.example.com/v1/downloads/presign",
|
|
867
|
+
"httpMethod": "GET",
|
|
868
|
+
"apiKey": "{PRESIGN_API_KEY}",
|
|
869
|
+
"apiKeyHeader": "x-api-key",
|
|
870
|
+
"fileNameParam": "key",
|
|
871
|
+
"signedUrlField": "data.url"
|
|
872
|
+
},
|
|
873
|
+
"_version": 1
|
|
874
|
+
}
|
|
875
|
+
```
|
|
876
|
+
|
|
877
|
+
The producer's `settings.fileKey` is the name sent to the minting endpoint. All the usual file types and compression apply — the file is staged locally and parsed exactly as one from S3 or disk.
|
|
878
|
+
|
|
879
|
+
### **Limits**
|
|
880
|
+
|
|
881
|
+
- **One file per name, no patterns.** A `%` pattern in `fileKey` is refused: the minting endpoint signs one named object and Remora cannot list the others. Use `aws-s3` or `local` when you need a pattern.
|
|
882
|
+
- **5 GB per object.** A signed `PUT` is a single request, which is S3's limit for one. Set the consumer's `split.maxFileSizeGb` below 5 and the normal rotation ships larger exports as several objects, each with its own URL. Exceeding 5 GB in a *single* object would need presigned multipart, which your endpoint would have to implement as three separate operations.
|
|
883
|
+
- **No resume.** A dropped connection restarts the file, so prefer a smaller `split.maxFileSizeGb` here than you would on `aws-s3`, which uploads in 5 MB parts.
|
|
884
|
+
- **No connection test at startup.** Unlike `http-api`, this engine does not probe on `init`: minting is not a free health check, since each call leaves a live signed URL behind. The first real transfer is the test.
|
|
885
|
+
|
|
886
|
+
---
|
|
887
|
+
|
|
888
|
+
## **AWS SQS Sources**
|
|
889
|
+
|
|
890
|
+
SQS sources point at an Amazon SQS queue. They are used to drive consumers from queue messages (see consumer **QUEUE triggers**) rather than to extract producer data.
|
|
891
|
+
|
|
892
|
+
### **Configuration Schema**
|
|
893
|
+
|
|
894
|
+
```json
|
|
895
|
+
{
|
|
896
|
+
"$schema": "https://raw.githubusercontent.com/ForzaLabs/remora-public/refs/heads/main/json_schemas/source-schema.json",
|
|
897
|
+
"name": "execute-queue",
|
|
898
|
+
"engine": "aws-sqs",
|
|
899
|
+
"authentication": {
|
|
900
|
+
"method": "access-secret-key",
|
|
901
|
+
"queue": "remora-execute-consumer",
|
|
902
|
+
"accountId": "{AWS_ACCOUNT_ID}",
|
|
903
|
+
"region": "us-east-1",
|
|
904
|
+
"accessKey": "{AWS_ACCESS_KEY_ID}",
|
|
905
|
+
"secretKey": "{AWS_SECRET_ACCESS_KEY}"
|
|
906
|
+
},
|
|
907
|
+
"_version": 1
|
|
908
|
+
}
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
### **Authentication Properties**
|
|
912
|
+
|
|
913
|
+
| Property | Required | Description | Example |
|
|
914
|
+
| --- | --- | --- | --- |
|
|
915
|
+
| `method` | ✅ | `"access-secret-key"`, `"iam"`, or `"arn"` | `"access-secret-key"` |
|
|
916
|
+
| `queue` | ✅ | Queue URL, or queue name (combined with `accountId` + `region`) | `"remora-execute-consumer"` |
|
|
917
|
+
| `region` | ✅ | AWS region of the queue | `"us-east-1"` |
|
|
918
|
+
| `accountId` | ❌ | AWS account ID, used to build the queue URL from a queue name | `"{AWS_ACCOUNT_ID}"` |
|
|
919
|
+
| `accessKey` / `secretKey` | ❌ | Static credentials (for `access-secret-key`) | `"{AWS_ACCESS_KEY_ID}"` |
|
|
920
|
+
| `roleArn` | ❌ | Role to assume for `arn` (see [IAM Role Authentication](#iam-role-authentication)) | `"arn:aws:iam::...:role/remora-sqs"` |
|
|
921
|
+
|
|
922
|
+
### **As the execute-consumer API queue**
|
|
923
|
+
|
|
924
|
+
Pointing the project's `API_QUEUE` setting at an SQS source turns that queue into an alternative to
|
|
925
|
+
the HTTP execute-consumer API: each message names a consumer to run.
|
|
926
|
+
|
|
927
|
+
A message may also carry an `options` object asking the run for extra filters and a record limit,
|
|
928
|
+
on the same terms as the HTTP API — see
|
|
929
|
+
[Run Options](Remora%20-%20Consumers.md) under the consumer's data filtering. The options are
|
|
930
|
+
checked against the named consumer before the run reads anything, and a command carrying an invalid
|
|
931
|
+
one fails without executing.
|
|
932
|
+
|
|
933
|
+
```json
|
|
934
|
+
{
|
|
935
|
+
"settings": {
|
|
936
|
+
"SQL_MAX_QUERY_ROWS": 10000,
|
|
937
|
+
"API_QUEUE": {
|
|
938
|
+
"source": "execute-queue",
|
|
939
|
+
"deadLetterQueue": "remora-execute-consumer-dlq"
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
```
|
|
944
|
+
|
|
945
|
+
| Property | Required | Description |
|
|
946
|
+
| --- | --- | --- |
|
|
947
|
+
| `source` | ✅ | Name of the `aws-sqs` source to poll |
|
|
948
|
+
| `deadLetterQueue` | ❌ | Queue URL, or queue name resolved against the source's `region` and `accountId`. A failed command is moved here instead of being deleted, so it can be inspected and replayed |
|
|
949
|
+
|
|
950
|
+
### **Failed commands**
|
|
951
|
+
|
|
952
|
+
Each message on this queue is a **command**: it asks for one consumer run. So it is removed from the
|
|
953
|
+
queue once it has run, whether the run succeeded or failed — a redelivery would be a second
|
|
954
|
+
execution, not a resumption of the first, and the usual failures here (a consumer that no longer
|
|
955
|
+
exists, a malformed payload, a source that is down) are not ones another delivery fixes.
|
|
956
|
+
|
|
957
|
+
That is the opposite of a consumer `QUEUE` trigger, where a failed message *is* retried — see
|
|
958
|
+
[Failed messages](Remora%20-%20Consumers.md) under the consumer QUEUE triggers. There is therefore no
|
|
959
|
+
delivery count to bound here, only a destination: with `deadLetterQueue` set, a failed command is
|
|
960
|
+
copied there (carrying `remoraSourceQueue`, `remoraSourceMessageId` and `remoraReceiveCount` as
|
|
961
|
+
message attributes) and then deleted; without one, it is deleted outright and the only trace is the
|
|
962
|
+
worker's log. Configuring one is worth it for anything that has to be diagnosed or replayed.
|
|
963
|
+
|
|
964
|
+
---
|
|
965
|
+
|
|
966
|
+
|
|
967
|
+
## **Common Configuration Patterns**
|
|
968
|
+
|
|
969
|
+
### **Environment Variable Usage**
|
|
970
|
+
|
|
971
|
+
**Recommended Pattern:**
|
|
972
|
+
|
|
973
|
+
```json
|
|
974
|
+
{
|
|
975
|
+
"authentication": {
|
|
976
|
+
"accessKey": "{AWS_ACCESS_KEY_ID}",
|
|
977
|
+
"secretKey": "{AWS_SECRET_ACCESS_KEY}",
|
|
978
|
+
"sessionToken": "{AWS_SESSION_TOKEN}"
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
```
|
|
982
|
+
|
|
983
|
+
**Environment Setup:**
|
|
984
|
+
|
|
985
|
+
```bash
|
|
986
|
+
# AWS Credentialsexport AWS_ACCESS_KEY_ID=AKIA...
|
|
987
|
+
export AWS_SECRET_ACCESS_KEY=wJa...
|
|
988
|
+
export AWS_SESSION_TOKEN=IQoJ...
|
|
989
|
+
|
|
990
|
+
# Delta Shareexport DELTA_TOKEN=dapi...
|
|
991
|
+
|
|
992
|
+
# Custom Variablesexport PROD_DATABASE_HOST=prod-db.company.com
|
|
993
|
+
export DEV_BUCKET_NAME=dev-data-bucket
|
|
994
|
+
```
|
|
995
|
+
|
|
996
|
+
### **Multi-Environment Configuration**
|
|
997
|
+
|
|
998
|
+
**Development:**
|
|
999
|
+
|
|
1000
|
+
```json
|
|
1001
|
+
{
|
|
1002
|
+
"name": "dev-data",
|
|
1003
|
+
"description": "Development environment data source",
|
|
1004
|
+
"engine": "local",
|
|
1005
|
+
"authentication": {
|
|
1006
|
+
"method": "implicit",
|
|
1007
|
+
"path": "./dev-data"
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
```
|
|
1011
|
+
|
|
1012
|
+
**Staging:**
|
|
1013
|
+
|
|
1014
|
+
```json
|
|
1015
|
+
{
|
|
1016
|
+
"name": "staging-s3",
|
|
1017
|
+
"description": "Staging environment S3 bucket",
|
|
1018
|
+
"engine": "aws-s3",
|
|
1019
|
+
"authentication": {
|
|
1020
|
+
"method": "access-secret-key",
|
|
1021
|
+
"region": "us-east-1",
|
|
1022
|
+
"accessKey": "{STAGING_ACCESS_KEY}",
|
|
1023
|
+
"secretKey": "{STAGING_SECRET_KEY}",
|
|
1024
|
+
"bucket": "staging-data-bucket"
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
```
|
|
1028
|
+
|
|
1029
|
+
**Production:**
|
|
1030
|
+
|
|
1031
|
+
```json
|
|
1032
|
+
{
|
|
1033
|
+
"name": "prod-warehouse",
|
|
1034
|
+
"description": "Production data warehouse",
|
|
1035
|
+
"engine": "aws-redshift",
|
|
1036
|
+
"authentication": {
|
|
1037
|
+
"method": "access-secret-key",
|
|
1038
|
+
"region": "us-east-1",
|
|
1039
|
+
"accessKey": "{PROD_ACCESS_KEY}",
|
|
1040
|
+
"secretKey": "{PROD_SECRET_KEY}",
|
|
1041
|
+
"database": "production_db",
|
|
1042
|
+
"workgroup": "production-workgroup"
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
```
|
|
1046
|
+
|
|
1047
|
+
---
|
|
1048
|
+
|
|
1049
|
+
## **Validation and Testing**
|
|
1050
|
+
|
|
1051
|
+
### **Configuration Validation**
|
|
1052
|
+
|
|
1053
|
+
Use the Remora CLI to validate your source configurations:
|
|
1054
|
+
|
|
1055
|
+
```bash
|
|
1056
|
+
# Compile & validate every resource configuration in the project
|
|
1057
|
+
# (includes source checks, e.g. required AWS credential fields per method)
|
|
1058
|
+
remora compile
|
|
1059
|
+
```
|
|
1060
|
+
|
|
1061
|
+
### **Connection Testing**
|
|
1062
|
+
|
|
1063
|
+
There is no standalone source-connectivity command. To exercise a source end-to-end, run a producer that uses it and inspect the extracted rows:
|
|
1064
|
+
|
|
1065
|
+
```bash
|
|
1066
|
+
# Read a few records through a producer that uses the source
|
|
1067
|
+
remora sample my-producer -s 10
|
|
1068
|
+
```
|
|
1069
|
+
|
|
1070
|
+
If the source is misconfigured (bad credentials, missing bucket, expired token, …) the command fails with the underlying connection error.
|
|
1071
|
+
|
|
1072
|
+
### **Common Validation Errors**
|
|
1073
|
+
|
|
1074
|
+
| Error | Cause | Solution |
|
|
1075
|
+
| --- | --- | --- |
|
|
1076
|
+
| `Invalid AWS credentials` | Wrong access keys | Check environment variables |
|
|
1077
|
+
| `Bucket not found` | Incorrect bucket name | Verify bucket exists and region |
|
|
1078
|
+
| `Permission denied` | Insufficient IAM permissions | Review and update IAM policies |
|
|
1079
|
+
| `Invalid workgroup` | Wrong Redshift workgroup | Check workgroup name and region |
|
|
1080
|
+
| `Invalid token` | Expired Delta Share token | Refresh authentication token |
|
|
1081
|
+
|
|
1082
|
+
---
|
|
1083
|
+
|
|
1084
|
+
## **Performance Optimization**
|
|
1085
|
+
|
|
1086
|
+
### **Memory Management**
|
|
1087
|
+
|
|
1088
|
+
- **File Processing**: Large files are streamed to minimize memory usage
|
|
1089
|
+
- **Query Results**: Limited by `SQL_MAX_QUERY_ROWS` setting
|
|
1090
|
+
- **Parallel Processing**: Remora automatically multithread large file for computing optimization
|
|
1091
|
+
|
|
1092
|
+
### **Connection Pooling**
|
|
1093
|
+
|
|
1094
|
+
- **Reuse connections** across multiple operations
|
|
1095
|
+
- **Connection warming** during initialization
|
|
1096
|
+
- **Automatic retry** for transient failures
|
|
1097
|
+
|
|
1098
|
+
### **Best Practices**
|
|
1099
|
+
|
|
1100
|
+
1. **Use appropriate source types** for your data storage pattern
|
|
1101
|
+
2. **Configure memory limits** based on available system resources
|
|
1102
|
+
3. **Implement proper error handling** in your data pipelines
|
|
1103
|
+
4. **Monitor connection health** in production environments
|
|
1104
|
+
5. **Use environment variables** for sensitive configuration values
|
|
1105
|
+
|
|
1106
|
+
---
|
|
1107
|
+
|
|
1108
|
+
## **Troubleshooting**
|
|
1109
|
+
|
|
1110
|
+
### **Common Issues**
|
|
1111
|
+
|
|
1112
|
+
**Connection Timeouts:**
|
|
1113
|
+
|
|
1114
|
+
- Increase `DEFAULT_TIMEOUT_SECONDS` in project configuration
|
|
1115
|
+
- Check network connectivity to source systems
|
|
1116
|
+
- Verify firewall and security group settings
|
|
1117
|
+
|
|
1118
|
+
**Authentication Failures:**
|
|
1119
|
+
|
|
1120
|
+
- Verify environment variables are set correctly
|
|
1121
|
+
- Check IAM permissions for AWS sources
|
|
1122
|
+
- Ensure tokens are not expired for Delta Share
|
|
1123
|
+
|
|
1124
|
+
**File Not Found Errors:**
|
|
1125
|
+
|
|
1126
|
+
- Verify file paths exist for local sources
|
|
1127
|
+
- Check S3 object keys and bucket permissions
|
|
1128
|
+
- Ensure proper case sensitivity in file names
|
|
1129
|
+
|
|
1130
|
+
### **Debug Mode**
|
|
1131
|
+
|
|
1132
|
+
Enable debug logging in your project configuration:
|
|
1133
|
+
|
|
1134
|
+
```json
|
|
1135
|
+
{
|
|
1136
|
+
"settings": {
|
|
1137
|
+
"LOG_LEVEL": "DEBUG",
|
|
1138
|
+
"DEBUG_MODE": true}
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
```
|
|
1142
|
+
|
|
1143
|
+
### **Support Resources**
|
|
1144
|
+
|
|
1145
|
+
- **Schema Validation**: All configurations are validated against JSON schemas
|
|
1146
|
+
- **CLI Help**: Run `remora --help` for command documentation
|
|
1147
|
+
- **Error Messages**: Include specific guidance for resolution
|
|
1148
|
+
- **Connection errors**: Run a producer with `remora sample <producer>` to surface source connection issues
|
|
1149
|
+
|
|
1150
|
+
---
|