@infinitetoken/lumber-kit 0.6.0 → 0.8.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/README.md +40 -11
- package/contract/README.md +21 -0
- package/contract/schema.json +2317 -0
- package/contract/vectors.json +180 -0
- package/dist/index.d.mts +116 -44
- package/dist/index.d.ts +116 -44
- package/dist/index.js +73 -7
- package/dist/index.mjs +64 -7
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# @infinitetoken/lumber-kit
|
|
2
2
|
|
|
3
|
-
Shared TypeScript types, wire format contract, and query interfaces for the [Lumber](https://
|
|
3
|
+
Shared TypeScript types, wire format contract, and query interfaces for the [Lumber](https://lumber.infinitetoken.com) cloud logging suite. Lumber is end-to-end encrypted: a client encrypts every log with AES-256-GCM before it leaves the process, and the server stores only ciphertext plus each log's level.
|
|
4
|
+
|
|
5
|
+
To send logs from your own code, you want a client rather than this package: [`@infinitetoken/lumber`](https://github.com/infinitetoken/Lumber-Node) for Node.js, [`lumber-cloud`](https://github.com/infinitetoken/Lumber-Python) for Python or [`lumber-go`](https://github.com/infinitetoken/lumber-go) for Go. The kit is what those clients and Lumber's own apps and services share, and its [contract](contract/README.md) is what a client in any language is tested against.
|
|
4
6
|
|
|
5
7
|
## Install
|
|
6
8
|
|
|
@@ -84,6 +86,10 @@ import { LogLevel, LogRecord, LogEncrypted, LogPayload, LogsQuery } from '@infin
|
|
|
84
86
|
| `AppRecord` | interface | API response shape for an app |
|
|
85
87
|
| `AppCreatedRecord` | interface | POST /apps 201 response (includes `encryptedKey` once) |
|
|
86
88
|
| `UserRecord` | interface | API response shape for a user |
|
|
89
|
+
| `TimeFormat` | interface | How a user reads times on every device: `{ clock: '24h' \| '12h', zone: 'local' \| 'utc' }` |
|
|
90
|
+
| `DEFAULT_TIME_FORMAT` | constant | 24-hour, local: what a user gets until they pick one |
|
|
91
|
+
| `isValidTimeFormat` | function | A known clock and zone, and nothing else |
|
|
92
|
+
| `normalizeTimeFormat` | function | A stored or sent time format, each part the default when missing or unknown |
|
|
87
93
|
| `TeamRecord` | interface | API response shape for a team |
|
|
88
94
|
| `TeamMemberRecord` | interface | Team membership record (listed members carry name, handle, color and picture) |
|
|
89
95
|
| `AppTeamAccess` | type | Who on an app's team reads it: `'everyone'` or `'owner'` |
|
|
@@ -125,6 +131,19 @@ import { LogLevel, LogRecord, LogEncrypted, LogPayload, LogsQuery } from '@infin
|
|
|
125
131
|
|
|
126
132
|
A plugin sends only the token to the API (`Authorization: Bearer <token>`) and encrypts with the rest, so the encryption key never leaves it.
|
|
127
133
|
|
|
134
|
+
### Plugins
|
|
135
|
+
|
|
136
|
+
| Export | Kind | Purpose |
|
|
137
|
+
|--------|------|---------|
|
|
138
|
+
| `LogIngestBody` | interface | `POST /logs`' body: `{ level, encrypted }` |
|
|
139
|
+
| `LogData` | interface | What a log call takes beside its message: `requestId`, `traceId`, `context`, `metadata` and any other keys |
|
|
140
|
+
| `buildLogPayload` | function | The `LogPayload` a log call encrypts, from its level, message, data and the plugin's automatic context |
|
|
141
|
+
| `DEFAULT_API_ENDPOINT` | constant | `https://api.lumber.infinitetoken.com` |
|
|
142
|
+
| `LOG_KEY_VERSION` | constant | `LogEncrypted`'s `keyVersion` (1) |
|
|
143
|
+
| `logDelivery` | object | Attempts, retry delays and the pause after a 429 |
|
|
144
|
+
| `logDeliveryOutcome` | function | `sent`, `retry`, `drop` or `pause` for a `POST /logs` status (null for a network error) |
|
|
145
|
+
| `logRetryDelayMs` | function | The wait after a log's nth failed attempt: 1s, 2s, 4s, up to 30s |
|
|
146
|
+
|
|
128
147
|
## Wire format
|
|
129
148
|
|
|
130
149
|
All log content is E2E encrypted before leaving the client. `LogRecord` contains only:
|
|
@@ -135,14 +154,14 @@ All log content is E2E encrypted before leaving the client. `LogRecord` contains
|
|
|
135
154
|
createdAt: string // ISO 8601
|
|
136
155
|
updatedAt: string // ISO 8601
|
|
137
156
|
appId: string
|
|
138
|
-
encrypted: LogEncrypted // opaque
|
|
157
|
+
encrypted: LogEncrypted // opaque to the server, which can't read it
|
|
139
158
|
}
|
|
140
159
|
```
|
|
141
160
|
|
|
142
161
|
`LogEncrypted` uses AES-256-GCM:
|
|
143
|
-
- `nonce
|
|
144
|
-
- `payload
|
|
145
|
-
- `keyVersion
|
|
162
|
+
- `nonce`: base64(random IV, 12 bytes)
|
|
163
|
+
- `payload`: base64(authTag[16 bytes] + ciphertext)
|
|
164
|
+
- `keyVersion`: always `1` for the current format; changes are semver major
|
|
146
165
|
|
|
147
166
|
The plaintext shape inside `payload` is `LogPayload`:
|
|
148
167
|
|
|
@@ -157,26 +176,36 @@ The plaintext shape inside `payload` is `LogPayload`:
|
|
|
157
176
|
}
|
|
158
177
|
```
|
|
159
178
|
|
|
179
|
+
## Other languages
|
|
180
|
+
|
|
181
|
+
TypeScript can't be imported from Python or Go, so the kit ships its contract in `contract/`: a JSON Schema of every exported type (`schema.json`, generated from the TypeScript by `npm run contract`) and known answers (`vectors.json`) for API keys, AES-256-GCM, payloads and delivery, which the kit's own tests check against its code. Lumber-Python and Lumber-Go test against a pinned copy. See [`contract/README.md`](contract/README.md).
|
|
182
|
+
|
|
160
183
|
## const enum and Babel consumers
|
|
161
184
|
|
|
162
185
|
`LogLevel` is a TypeScript `const enum`. TypeScript consumers have values inlined at compile time (zero runtime overhead).
|
|
163
186
|
|
|
164
187
|
If your project uses Babel, esbuild, or `isolatedModules: true`, the runtime enum object is available because Lumber-Kit is compiled with `preserveConstEnums: true`. No special configuration is needed.
|
|
165
188
|
|
|
166
|
-
##
|
|
189
|
+
## Developing
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
npm install
|
|
193
|
+
npm run verify # lint, test, typecheck, build
|
|
194
|
+
npm run contract # regenerate contract/schema.json after changing a type
|
|
195
|
+
```
|
|
167
196
|
|
|
168
|
-
|
|
197
|
+
To try a change in a project that uses the kit before it's published, use [yalc](https://github.com/wclr/yalc) rather than `npm link`, whose symlinks break Metro (React Native's bundler):
|
|
169
198
|
|
|
170
199
|
```bash
|
|
171
|
-
# In Lumber-Kit
|
|
200
|
+
# In Lumber-Kit
|
|
172
201
|
npm run build
|
|
173
202
|
yalc publish
|
|
174
203
|
|
|
175
|
-
# In
|
|
204
|
+
# In the project using it
|
|
176
205
|
yalc add @infinitetoken/lumber-kit
|
|
177
206
|
npm install
|
|
178
207
|
|
|
179
|
-
# After
|
|
208
|
+
# After each change to Lumber-Kit
|
|
180
209
|
npm run build
|
|
181
|
-
yalc push #
|
|
210
|
+
yalc push # updates every project that added it
|
|
182
211
|
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Lumber's contract
|
|
2
|
+
|
|
3
|
+
The shapes and known answers every Lumber client is tested against, whatever its language. Lumber-Kit's TypeScript is the source: these files are generated from it or checked against it by the kit's own tests, and they ship in the npm package, so a client pins them by the kit's version.
|
|
4
|
+
|
|
5
|
+
- `schema.json`: a JSON Schema (draft-07) of every type the kit exports, generated by `npm run contract`. A plugin sends `LogIngestBody` to `POST /logs` and encrypts a `LogPayload` into its `encrypted`. `LogPayload` allows no other keys, so a misspelled field (`request_id`) fails validation rather than going missing in the app.
|
|
6
|
+
- `vectors.json`: known answers.
|
|
7
|
+
- `constants`: the default endpoint, ingestion path, key version, key, nonce and tag sizes, log levels and the delivery policy.
|
|
8
|
+
- `apiKeys`: how an API key (`<token>.<encryptionKey>`) splits.
|
|
9
|
+
- `encryption`: AES-256-GCM with a fixed key and nonce, so a plugin's encryption has to give these exact bytes (`payload` is base64 of the 16-byte tag and then the ciphertext, `nonce` the 12-byte IV).
|
|
10
|
+
- `payloads`: the plaintext a log call builds from its level, message, data and the plugin's automatic context.
|
|
11
|
+
- `deliveryOutcomes` and `retryDelaysMs`: what each answer from `POST /logs` means, and how long to wait after each failure.
|
|
12
|
+
|
|
13
|
+
## In another language
|
|
14
|
+
|
|
15
|
+
Copy this folder into the client's tests at a pinned kit version, and test every vector against the client's own code. The kit's version is the contract's: when the kit changes a shape, the new version's files fail the client's tests until it catches up.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
curl -sL https://registry.npmjs.org/@infinitetoken/lumber-kit/-/lumber-kit-0.7.0.tgz | tar -xz --strip-components=1 package/contract
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Lumber-Python (`scripts/sync_contract.py`) and Lumber-Go (`scripts/sync-contract.sh`) each have a script that does this, and their CI fails when the copy differs from the pinned version's. Lumber-Node reads the contract straight from its installed kit (`@infinitetoken/lumber-kit/contract/vectors.json`).
|