@sequenceholdings/artifact-studio 0.3.0 → 0.3.1
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/VERSION-PIN.md +4 -0
- package/dist/cli.js +29 -3
- package/dist/gateway/admission.d.ts +2 -1
- package/dist/gateway/admission.js +3 -1
- package/dist/gateway/authorization-cache.d.ts +52 -0
- package/dist/gateway/authorization-cache.js +111 -0
- package/dist/gateway/control-admission.d.ts +48 -0
- package/dist/gateway/control-admission.js +127 -0
- package/dist/gateway/control.d.ts +4 -1
- package/dist/gateway/control.js +5 -2
- package/dist/gateway/hosted-proxy.js +4 -7
- package/dist/gateway/server.d.ts +9 -0
- package/dist/gateway/server.js +101 -17
- package/dist/gateway/session.d.ts +1 -0
- package/dist/gateway/session.js +2 -1
- package/dist/gateway/usage.d.ts +45 -12
- package/dist/gateway/usage.js +96 -26
- package/dist/gateway-main.js +35 -5
- package/dist/manifest-next.js +13 -1
- package/dist/next/bridge.js +8 -1
- package/dist/next-cache/config.js +31 -4
- package/dist/next-image-config.js +1 -1
- package/dist/server/client.d.ts +16 -2
- package/dist/server/request-context.d.ts +1 -0
- package/dist/server/request-context.js +4 -0
- package/dist/server/runtime-config.d.ts +1 -0
- package/dist/server.d.ts +1 -1
- package/dist/templates/nextjs/README.md +6 -0
- package/dist/templates/nextjs/gitignore +9 -0
- package/dist/templates/nextjs/pnpm-workspace.yaml +14 -0
- package/dist/trusted-install.js +62 -2
- package/dist/tunnel/broker.js +34 -22
- package/dist/tunnel/protocol.d.ts +9 -0
- package/dist/tunnel/protocol.js +31 -17
- package/dist/tunnel/redis-auth.d.ts +37 -0
- package/dist/tunnel/redis-auth.js +92 -0
- package/dist/tunnel/redis-types.d.ts +5 -0
- package/dist/tunnel/redis.d.ts +5 -2
- package/dist/tunnel/redis.js +19 -6
- package/package.json +1 -1
- package/templates/nextjs/README.md +6 -0
- package/templates/nextjs/gitignore +9 -0
- package/templates/nextjs/pnpm-workspace.yaml +14 -0
package/dist/tunnel/redis.d.ts
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import type { TunnelLease } from './broker.js';
|
|
2
2
|
import type { TunnelRelay, TunnelSocket } from './transport.js';
|
|
3
|
-
import {
|
|
3
|
+
import type { RedisTunnelRelayOptions } from './redis-types.js';
|
|
4
4
|
export { RedisTunnelOwnership } from './redis-ownership.js';
|
|
5
5
|
export type { RedisTunnelCommands } from './redis-ownership.js';
|
|
6
6
|
export type { RedisTunnelPublisher, RedisTunnelSubscriber, RedisTunnelRelayOptions } from './redis-types.js';
|
|
7
7
|
/** Internal Redis channels contain authorized application traffic. Deploy them
|
|
8
|
-
* on the environment's private, authenticated Redis and disable command replay.
|
|
8
|
+
* on the environment's private, authenticated Redis and disable command replay.
|
|
9
|
+
* Every message is authenticated with a gateway-only key: other Redis clients
|
|
10
|
+
* can neither inject, redirect nor replay tunnel frames. */
|
|
9
11
|
export declare class RedisTunnelRelay implements TunnelRelay {
|
|
10
12
|
private readonly options;
|
|
11
13
|
private started;
|
|
12
14
|
private closed;
|
|
13
15
|
private readonly attached;
|
|
14
16
|
private readonly connected;
|
|
17
|
+
private readonly frames;
|
|
15
18
|
private publishing;
|
|
16
19
|
private receiving;
|
|
17
20
|
private outgoingBytes;
|
package/dist/tunnel/redis.js
CHANGED
|
@@ -2,16 +2,19 @@ import { randomUUID } from 'node:crypto';
|
|
|
2
2
|
import { DEFAULT_TUNNEL_LIMITS, parseTunnelFrame } from './protocol.js';
|
|
3
3
|
import { redisIdentifier as identifier, redisNamespaceSchema as namespaceSchema, redisLeaseSchema as leaseSchema } from './redis-ownership.js';
|
|
4
4
|
import { RelaySocket } from './redis-socket.js';
|
|
5
|
-
import {
|
|
5
|
+
import { deriveRelayFrameKey, RelayFrameAuthenticator } from './redis-auth.js';
|
|
6
6
|
export { RedisTunnelOwnership } from './redis-ownership.js';
|
|
7
7
|
/** Internal Redis channels contain authorized application traffic. Deploy them
|
|
8
|
-
* on the environment's private, authenticated Redis and disable command replay.
|
|
8
|
+
* on the environment's private, authenticated Redis and disable command replay.
|
|
9
|
+
* Every message is authenticated with a gateway-only key: other Redis clients
|
|
10
|
+
* can neither inject, redirect nor replay tunnel frames. */
|
|
9
11
|
export class RedisTunnelRelay {
|
|
10
12
|
options;
|
|
11
13
|
started = false;
|
|
12
14
|
closed = false;
|
|
13
15
|
attached = new Map();
|
|
14
16
|
connected = new Map();
|
|
17
|
+
frames;
|
|
15
18
|
publishing = Promise.resolve();
|
|
16
19
|
receiving = Promise.resolve();
|
|
17
20
|
outgoingBytes = 0;
|
|
@@ -24,6 +27,7 @@ export class RedisTunnelRelay {
|
|
|
24
27
|
namespaceSchema.parse(options.namespace);
|
|
25
28
|
if (options.namespace !== options.ownership.namespace || Object.is(options.publisher, options.subscriber))
|
|
26
29
|
throw new Error('Separate, environment-scoped Redis clients are required');
|
|
30
|
+
this.frames = new RelayFrameAuthenticator(deriveRelayFrameKey({ secret: options.secret, namespace: options.namespace }));
|
|
27
31
|
if (options.connectTimeoutMs !== undefined && (!Number.isSafeInteger(options.connectTimeoutMs) || options.connectTimeoutMs < 1 || options.connectTimeoutMs > 60_000))
|
|
28
32
|
throw new Error('Invalid tunnel connect timeout');
|
|
29
33
|
const policy = options.publisher.options;
|
|
@@ -93,8 +97,15 @@ export class RedisTunnelRelay {
|
|
|
93
97
|
onMessage = (channel, raw) => {
|
|
94
98
|
if (!this.started || channel !== this.channel(this.options.nodeId))
|
|
95
99
|
return;
|
|
100
|
+
// Authenticate before anything else: a message without a valid MAC, fresh timestamp and new
|
|
101
|
+
// sequence is dropped without resetting live traffic, so a Redis client cannot inject or DoS.
|
|
102
|
+
const envelope = this.frames.open({ channel, message: raw });
|
|
103
|
+
if (!envelope) {
|
|
104
|
+
this.options.onRejectedFrame?.();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
96
107
|
const bytes = Buffer.byteLength(raw);
|
|
97
|
-
if (
|
|
108
|
+
if (this.incomingBytes + bytes > DEFAULT_TUNNEL_LIMITS.maxBufferedBytes) {
|
|
98
109
|
this.onRelayOverload();
|
|
99
110
|
return;
|
|
100
111
|
}
|
|
@@ -102,11 +113,13 @@ export class RedisTunnelRelay {
|
|
|
102
113
|
const generation = this.generation;
|
|
103
114
|
this.receiving = this.receiving.then(async () => {
|
|
104
115
|
if (this.started && generation === this.generation)
|
|
105
|
-
await this.receive(
|
|
116
|
+
await this.receive(envelope);
|
|
106
117
|
}).catch(() => this.onRelayOverload()).finally(() => { this.incomingBytes -= bytes; });
|
|
107
118
|
};
|
|
108
119
|
publish(nodeId, envelope) {
|
|
109
|
-
const
|
|
120
|
+
const channel = this.channel(nodeId);
|
|
121
|
+
// Sealed in call order, so each destination sees this node's sequence numbers ascending.
|
|
122
|
+
const payload = this.frames.seal({ channel, envelope });
|
|
110
123
|
const bytes = Buffer.byteLength(payload);
|
|
111
124
|
if (this.outgoingBytes + bytes > DEFAULT_TUNNEL_LIMITS.maxBufferedBytes)
|
|
112
125
|
return Promise.reject(new Error('Tunnel relay publish queue exceeded'));
|
|
@@ -115,7 +128,7 @@ export class RedisTunnelRelay {
|
|
|
115
128
|
const sending = this.publishing.then(async () => {
|
|
116
129
|
if (envelope.type !== 'close' && (generation !== this.generation || !this.started || this.closed))
|
|
117
130
|
throw new Error('Tunnel relay generation changed');
|
|
118
|
-
if (await this.options.publisher.publish(
|
|
131
|
+
if (await this.options.publisher.publish(channel, payload) < 1)
|
|
119
132
|
throw new Error('Tunnel node is offline');
|
|
120
133
|
});
|
|
121
134
|
this.publishing = sending.catch(() => undefined).finally(() => { this.outgoingBytes -= bytes; });
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"publishConfig": {
|
|
10
10
|
"access": "public"
|
|
11
11
|
},
|
|
12
|
-
"version": "0.3.
|
|
12
|
+
"version": "0.3.1",
|
|
13
13
|
"description": "SDK types and CLI library for building Artifact Studio apps. Driven via `seq-studio artifact <sub>` (@sequenceholdings/studio-cli), which runs the `./cli` runCli export.",
|
|
14
14
|
"type": "module",
|
|
15
15
|
"exports": {
|
|
@@ -71,6 +71,12 @@ only the capabilities the application needs, then configure authorized resource
|
|
|
71
71
|
bindings in Atlas. Local development uses explicit nonproduction resources and
|
|
72
72
|
never falls back to production when a binding is missing.
|
|
73
73
|
|
|
74
|
+
ORM access is declared per table and per mutation: list `<namespace>/<table>`
|
|
75
|
+
under `data.read` for every table a query reads (queries that follow
|
|
76
|
+
platform-user relations also read `<namespace>/platformUsers`). Under
|
|
77
|
+
`data.write`, use `<namespace>/ops/<Operation>` for each persisted mutation the
|
|
78
|
+
app runs, or `<namespace>/<table>` for table-level coverage.
|
|
79
|
+
|
|
74
80
|
Managed secrets stay in hosted runtimes. For local integration tests, use your
|
|
75
81
|
own test credentials, mocks or an existing governed function. Use a hosted
|
|
76
82
|
preview for application code that needs a managed secret. Never put secrets in
|
|
@@ -2,7 +2,21 @@ packages:
|
|
|
2
2
|
- '.'
|
|
3
3
|
|
|
4
4
|
minimumReleaseAge: 10080
|
|
5
|
+
# The Sequence SDK chain is published from the Studio repo's audited release
|
|
6
|
+
# pipeline, so a new artifact-studio release must install without waiting out
|
|
7
|
+
# the seven-day quarantine. Scoped to the exact first-party packages a fresh
|
|
8
|
+
# scaffold resolves, not @sequenceholdings/*.
|
|
9
|
+
minimumReleaseAgeExclude:
|
|
10
|
+
- '@sequenceholdings/artifact-studio'
|
|
11
|
+
- '@sequenceholdings/atlas-ui'
|
|
12
|
+
- '@sequenceholdings/custom-roles-manifest'
|
|
13
|
+
- '@sequenceholdings/lattice-form-renderer'
|
|
14
|
+
- '@sequenceholdings/orm'
|
|
15
|
+
- '@sequenceholdings/storage'
|
|
5
16
|
strictDepBuilds: true
|
|
6
17
|
allowBuilds:
|
|
7
18
|
'@tailwindcss/oxide': false
|
|
19
|
+
# Reached through the SDK's static builder and ORM tooling, never run by a
|
|
20
|
+
# Next.js app; its platform binary installs as an optional dependency.
|
|
21
|
+
esbuild: false
|
|
8
22
|
sharp: false
|