@farthershore/backend 0.1.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 +71 -0
- package/dist/adapters/express.js +101 -0
- package/dist/generated/runtime-contract.js +239 -0
- package/dist/index.js +1496 -0
- package/dist/types/adapters/express.d.ts +37 -0
- package/dist/types/core/bootstrap.d.ts +40 -0
- package/dist/types/core/errors.d.ts +15 -0
- package/dist/types/core/health.d.ts +23 -0
- package/dist/types/core/jwks.d.ts +48 -0
- package/dist/types/core/metering.d.ts +49 -0
- package/dist/types/core/nonceCache.d.ts +29 -0
- package/dist/types/core/runtime.d.ts +96 -0
- package/dist/types/core/shutdown.d.ts +10 -0
- package/dist/types/core/tunnel.d.ts +159 -0
- package/dist/types/core/verifyRequest.d.ts +54 -0
- package/dist/types/generated/metering-contract.d.ts +36 -0
- package/dist/types/generated/runtime-contract.d.ts +191 -0
- package/dist/types/index.d.ts +34 -0
- package/dist/types/legacy/metering.d.ts +20 -0
- package/dist/types/runtime-signing.d.ts +25 -0
- package/dist/types/runtime-types.d.ts +137 -0
- package/package.json +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# @farthershore/backend
|
|
2
|
+
|
|
3
|
+
The secure BYO-backend runtime SDK. Install one package, set one token
|
|
4
|
+
(`FS_RUNTIME_TOKEN`), and Farther Shore protects (signs every gateway→backend
|
|
5
|
+
request; the SDK verifies and rejects anything not from Farther Shore) and meters
|
|
6
|
+
your backend.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
npm install @farthershore/backend
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Quick start (Express)
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { fartherShore } from "@farthershore/backend";
|
|
18
|
+
|
|
19
|
+
const fs = fartherShore.initFromEnv(); // derives everything from FS_RUNTIME_TOKEN
|
|
20
|
+
|
|
21
|
+
app.use(fs.middleware()); // fail-closed verify → req.fartherShore
|
|
22
|
+
|
|
23
|
+
app.post("/v1/runs", async (req, res) => {
|
|
24
|
+
const result = await runWorkflow(req.body);
|
|
25
|
+
await fs.meter("tokens", result.tokensUsed, {
|
|
26
|
+
requestId: req.fartherShore.requestId,
|
|
27
|
+
});
|
|
28
|
+
res.json(result);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
app.listen(3000);
|
|
32
|
+
process.on("SIGTERM", () => void fs.shutdown());
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## What `initFromEnv()` derives
|
|
36
|
+
|
|
37
|
+
The builder configures exactly one thing: `FS_RUNTIME_TOKEN`. Everything else —
|
|
38
|
+
product/backend/environment ids, the JWKS url, the metering endpoint and
|
|
39
|
+
credential, verification config, transport — is fetched from
|
|
40
|
+
`POST /v1/runtime/bootstrap` and cached in memory (refreshed lazily).
|
|
41
|
+
|
|
42
|
+
## Verification (fail-closed, always)
|
|
43
|
+
|
|
44
|
+
`fs.middleware()` (Express) and the framework-neutral
|
|
45
|
+
`fs.verifyRequest({ method, path, query, headers, body })` recompute the
|
|
46
|
+
[canonical signing string](../../docs/superpowers/specs/backend-runtime-spec.md)
|
|
47
|
+
from the actual request and verify the gateway's Ed25519 signature against a
|
|
48
|
+
JWKS-resolved public key. The plaintext `X-FS-*` headers are **untrusted** —
|
|
49
|
+
identity comes only from a signature whose claims match the real request.
|
|
50
|
+
|
|
51
|
+
Every failure (missing / malformed / bad-signature / stale / clock-skew /
|
|
52
|
+
wrong-route / body-hash-mismatch / replayed-nonce / unknown-kid /
|
|
53
|
+
jwks-unavailable) returns a typed `FartherShoreError` → **HTTP 401** (413 for
|
|
54
|
+
oversized bodies). There is no fail-open branch.
|
|
55
|
+
|
|
56
|
+
## Metering (billing-only)
|
|
57
|
+
|
|
58
|
+
`fs.meter(meter, qty, { requestId, routeId })` enqueues an idempotent event and
|
|
59
|
+
POSTs it to `/v1/metering/events` with a reusable bearer credential. Delivery is
|
|
60
|
+
at-least-once; the `event_id` idempotency key keeps core's ingest safe. Values
|
|
61
|
+
are tallied/billed post-cycle, not real-time enforced.
|
|
62
|
+
|
|
63
|
+
## Migrating from `@farthershore/metering`
|
|
64
|
+
|
|
65
|
+
`@farthershore/metering` is now a deprecated re-export shim of this package and
|
|
66
|
+
will be removed after the metering cutover. Replace the import; for new code,
|
|
67
|
+
prefer `fs.meter("tokens", n)` over the response-header signer.
|
|
68
|
+
|
|
69
|
+
The signing primitives and contract constants are re-exported from
|
|
70
|
+
`@farthershore/contracts/runtime`, the language-neutral source of truth every
|
|
71
|
+
SDK (TypeScript first; Go/Python/Java/Rust later) implements.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { createRequire as __createRequire } from "node:module";const require=__createRequire(import.meta.url);
|
|
2
|
+
|
|
3
|
+
// src/core/errors.ts
|
|
4
|
+
var FartherShoreError = class extends Error {
|
|
5
|
+
code;
|
|
6
|
+
status;
|
|
7
|
+
constructor(code, message, status) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = "FartherShoreError";
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.status = status ?? statusForCode(code);
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
function statusForCode(code) {
|
|
15
|
+
return code === "body_too_large" ? 413 : 401;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// src/generated/runtime-contract.ts
|
|
19
|
+
var RUNTIME_BODY_HASH_CONTRACT = {
|
|
20
|
+
algorithm: "SHA-256",
|
|
21
|
+
encoding: "hex-lower",
|
|
22
|
+
source: "raw-request-bytes",
|
|
23
|
+
emptyBodyHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
|
24
|
+
maxBodyBytes: 10485760,
|
|
25
|
+
streamingExemptToken: "STREAM",
|
|
26
|
+
streamingExemptContentTypes: [
|
|
27
|
+
"text/event-stream",
|
|
28
|
+
"application/octet-stream",
|
|
29
|
+
"multipart/form-data"
|
|
30
|
+
],
|
|
31
|
+
overMaxStatus: 413
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// src/adapters/express.ts
|
|
35
|
+
var STREAMING_CONTENT_TYPES = new Set(
|
|
36
|
+
RUNTIME_BODY_HASH_CONTRACT.streamingExemptContentTypes
|
|
37
|
+
);
|
|
38
|
+
function createExpressMiddleware(fs, options = {}) {
|
|
39
|
+
return (req, res, next) => {
|
|
40
|
+
void runMiddleware(fs, options, req, res, next);
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
async function runMiddleware(fs, options, req, res, next) {
|
|
44
|
+
try {
|
|
45
|
+
if (!options.always && !await fs.verificationRequired()) {
|
|
46
|
+
next();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const { path, query } = splitUrl(req);
|
|
50
|
+
const contentType = headerValue(req.headers, "content-type");
|
|
51
|
+
const streamingExempt = isStreamingExempt(contentType);
|
|
52
|
+
const body = streamingExempt ? null : extractRawBody(req);
|
|
53
|
+
const ctx = await fs.verifyRequest({
|
|
54
|
+
method: req.method,
|
|
55
|
+
path,
|
|
56
|
+
query,
|
|
57
|
+
headers: req.headers,
|
|
58
|
+
body,
|
|
59
|
+
streamingExempt
|
|
60
|
+
});
|
|
61
|
+
req.fartherShore = ctx;
|
|
62
|
+
next();
|
|
63
|
+
} catch (error) {
|
|
64
|
+
fail(res, error);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function fail(res, error) {
|
|
68
|
+
if (error instanceof FartherShoreError) {
|
|
69
|
+
res.status(error.status).json({ error: error.code });
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
res.status(401).json({ error: "bad_signature" });
|
|
73
|
+
}
|
|
74
|
+
function splitUrl(req) {
|
|
75
|
+
const raw = req.originalUrl ?? req.url ?? req.path ?? "/";
|
|
76
|
+
const qIndex = raw.indexOf("?");
|
|
77
|
+
if (qIndex === -1) return { path: raw, query: "" };
|
|
78
|
+
return { path: raw.slice(0, qIndex), query: raw.slice(qIndex + 1) };
|
|
79
|
+
}
|
|
80
|
+
function isStreamingExempt(contentType) {
|
|
81
|
+
if (!contentType) return false;
|
|
82
|
+
const base = contentType.split(";")[0].trim().toLowerCase();
|
|
83
|
+
return STREAMING_CONTENT_TYPES.has(base);
|
|
84
|
+
}
|
|
85
|
+
function extractRawBody(req) {
|
|
86
|
+
const raw = req.rawBody;
|
|
87
|
+
if (raw) return raw instanceof Uint8Array ? raw : new Uint8Array(raw);
|
|
88
|
+
const body = req.body;
|
|
89
|
+
if (body === void 0 || body === null) return null;
|
|
90
|
+
if (typeof body === "string") return new TextEncoder().encode(body);
|
|
91
|
+
if (body instanceof Uint8Array) return body;
|
|
92
|
+
return new Uint8Array(0);
|
|
93
|
+
}
|
|
94
|
+
function headerValue(headers, name) {
|
|
95
|
+
const value = headers[name] ?? headers[name.toLowerCase()];
|
|
96
|
+
if (Array.isArray(value)) return value[0];
|
|
97
|
+
return value;
|
|
98
|
+
}
|
|
99
|
+
export {
|
|
100
|
+
createExpressMiddleware
|
|
101
|
+
};
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { createRequire as __createRequire } from "node:module";const require=__createRequire(import.meta.url);
|
|
2
|
+
|
|
3
|
+
// src/generated/runtime-contract.ts
|
|
4
|
+
var RUNTIME_CONTRACT_VERSION = 1;
|
|
5
|
+
var RUNTIME_TOKEN_ENV = "FS_RUNTIME_TOKEN";
|
|
6
|
+
var RUNTIME_TOKEN_CONTRACT = {
|
|
7
|
+
environmentVariable: "FS_RUNTIME_TOKEN",
|
|
8
|
+
prefixes: {
|
|
9
|
+
live: "fsrt_live_",
|
|
10
|
+
test: "fsrt_test_"
|
|
11
|
+
},
|
|
12
|
+
opaque: true,
|
|
13
|
+
storage: "sha256-hash-only",
|
|
14
|
+
lastFour: true,
|
|
15
|
+
capabilities: ["gateway_verification", "metering", "health", "tunnel"]
|
|
16
|
+
};
|
|
17
|
+
var RUNTIME_BOOTSTRAP_CONTRACT = {
|
|
18
|
+
method: "POST",
|
|
19
|
+
path: "/v1/runtime/bootstrap",
|
|
20
|
+
authorization: "Bearer fsrt_...",
|
|
21
|
+
request: {
|
|
22
|
+
instanceId: "string?",
|
|
23
|
+
sdkVersion: "string?",
|
|
24
|
+
sdkLanguage: "string?"
|
|
25
|
+
},
|
|
26
|
+
response: {
|
|
27
|
+
product: {
|
|
28
|
+
id: "string",
|
|
29
|
+
slug: "string"
|
|
30
|
+
},
|
|
31
|
+
backend: {
|
|
32
|
+
id: "string",
|
|
33
|
+
slug: "string",
|
|
34
|
+
name: "string"
|
|
35
|
+
},
|
|
36
|
+
environment: {
|
|
37
|
+
id: "string?",
|
|
38
|
+
kind: "live | test"
|
|
39
|
+
},
|
|
40
|
+
capabilities: "string[]",
|
|
41
|
+
verification: {
|
|
42
|
+
required: "boolean",
|
|
43
|
+
jwksUrl: "string",
|
|
44
|
+
clockSkewSeconds: "number",
|
|
45
|
+
replayWindowSeconds: "number",
|
|
46
|
+
headerNames: "object"
|
|
47
|
+
},
|
|
48
|
+
metering: {
|
|
49
|
+
enabled: "boolean",
|
|
50
|
+
endpoint: "string",
|
|
51
|
+
credential: "string",
|
|
52
|
+
allowedMeters: "string[]",
|
|
53
|
+
allowedRoutes: "string[]",
|
|
54
|
+
perEventMax: "number"
|
|
55
|
+
},
|
|
56
|
+
transport: {
|
|
57
|
+
mode: "public_origin | mtls | cloudflare_tunnel",
|
|
58
|
+
runner: "managed_cloudflared | sidecar | null",
|
|
59
|
+
originUrl: "string?",
|
|
60
|
+
originHostname: "string?",
|
|
61
|
+
localTarget: "string?",
|
|
62
|
+
cloudflared: "object?"
|
|
63
|
+
},
|
|
64
|
+
routes: "object[]",
|
|
65
|
+
policyVersion: "string",
|
|
66
|
+
refreshAfterSeconds: "number"
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
var RUNTIME_SIGNING_CONTRACT = {
|
|
70
|
+
algorithm: "Ed25519",
|
|
71
|
+
encoding: "base64url",
|
|
72
|
+
keyMaterial: "service-jwt-jwks",
|
|
73
|
+
canonicalString: {
|
|
74
|
+
description: "Byte-exact, language-neutral serialization of the signed claim set. Fields are emitted in the FIXED order below, one per line, each as `name:value`, joined by a single newline (\\n, U+000A). NO trailing newline. The serialization depends on neither JSON key ordering nor any Node.js Buffer/serialization detail \u2014 only UTF-8 byte encoding of the field values. Empty/absent values are emitted as the empty string after the colon. Go/Python/Java/Rust reproduce this identically.",
|
|
75
|
+
fieldSeparator: "\n",
|
|
76
|
+
keyValueSeparator: ":",
|
|
77
|
+
trailingNewline: false,
|
|
78
|
+
fieldEncoding: "utf-8",
|
|
79
|
+
fields: [
|
|
80
|
+
"method",
|
|
81
|
+
"path",
|
|
82
|
+
"query",
|
|
83
|
+
"body-hash",
|
|
84
|
+
"request-id",
|
|
85
|
+
"timestamp",
|
|
86
|
+
"product-id",
|
|
87
|
+
"backend-id",
|
|
88
|
+
"route-id",
|
|
89
|
+
"policy-version"
|
|
90
|
+
],
|
|
91
|
+
fieldRules: {
|
|
92
|
+
method: "Uppercased HTTP method (e.g. GET, POST).",
|
|
93
|
+
path: "Request path, percent-encoded as received, no host, no query string. Always begins with '/'.",
|
|
94
|
+
query: "Canonical query string: parse pairs, sort by (name, then value) using byte (code-unit) order, re-join name=value pairs with '&'. Names and values are NOT re-encoded (passed through as received). Empty string when there is no query.",
|
|
95
|
+
"body-hash": "Lowercase hex SHA-256 of the RAW request body bytes. For an empty body, the SHA-256 of zero bytes (e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855). Streaming-exempt requests use the literal token 'STREAM'.",
|
|
96
|
+
"request-id": "Opaque unique request id minted by the gateway (also the replay-cache nonce).",
|
|
97
|
+
timestamp: "Integer Unix epoch seconds (UTC) at signing time, as a base-10 string with no padding.",
|
|
98
|
+
"product-id": "Product id the request is routed to.",
|
|
99
|
+
"backend-id": "Backend id the route binds to.",
|
|
100
|
+
"route-id": "Resolved route id; empty string if the route is unresolved.",
|
|
101
|
+
"policy-version": "Tenant artifact / policy version the gateway signed under."
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
var RUNTIME_CANONICAL_FIELDS = [
|
|
106
|
+
"method",
|
|
107
|
+
"path",
|
|
108
|
+
"query",
|
|
109
|
+
"body-hash",
|
|
110
|
+
"request-id",
|
|
111
|
+
"timestamp",
|
|
112
|
+
"product-id",
|
|
113
|
+
"backend-id",
|
|
114
|
+
"route-id",
|
|
115
|
+
"policy-version"
|
|
116
|
+
];
|
|
117
|
+
var RUNTIME_BODY_HASH_CONTRACT = {
|
|
118
|
+
algorithm: "SHA-256",
|
|
119
|
+
encoding: "hex-lower",
|
|
120
|
+
source: "raw-request-bytes",
|
|
121
|
+
emptyBodyHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
|
122
|
+
maxBodyBytes: 10485760,
|
|
123
|
+
streamingExemptToken: "STREAM",
|
|
124
|
+
streamingExemptContentTypes: [
|
|
125
|
+
"text/event-stream",
|
|
126
|
+
"application/octet-stream",
|
|
127
|
+
"multipart/form-data"
|
|
128
|
+
],
|
|
129
|
+
overMaxStatus: 413
|
|
130
|
+
};
|
|
131
|
+
var RUNTIME_HEADERS = {
|
|
132
|
+
signature: "x-fs-signature",
|
|
133
|
+
keyId: "x-fs-key-id",
|
|
134
|
+
requestId: "x-fs-request-id",
|
|
135
|
+
timestamp: "x-fs-timestamp",
|
|
136
|
+
productId: "x-fs-product-id",
|
|
137
|
+
backendId: "x-fs-backend-id",
|
|
138
|
+
routeId: "x-fs-route-id",
|
|
139
|
+
policyVersion: "x-fs-policy-version",
|
|
140
|
+
bodyHash: "x-fs-body-hash"
|
|
141
|
+
};
|
|
142
|
+
var RUNTIME_REPLAY_CONTRACT = {
|
|
143
|
+
windowSeconds: 300,
|
|
144
|
+
clockSkewSeconds: 5,
|
|
145
|
+
nonce: "x-fs-request-id",
|
|
146
|
+
nonceCache: "bounded-lru",
|
|
147
|
+
policy: "fail-closed"
|
|
148
|
+
};
|
|
149
|
+
var RUNTIME_ERROR_CODES = {
|
|
150
|
+
missingSignature: "missing_signature",
|
|
151
|
+
malformedSignature: "malformed_signature",
|
|
152
|
+
unknownKeyId: "unknown_key_id",
|
|
153
|
+
jwksUnavailable: "jwks_unavailable",
|
|
154
|
+
badSignature: "bad_signature",
|
|
155
|
+
bodyHashMismatch: "body_hash_mismatch",
|
|
156
|
+
routeMismatch: "route_mismatch",
|
|
157
|
+
clockSkew: "clock_skew",
|
|
158
|
+
expiredSignature: "expired_signature",
|
|
159
|
+
replayedNonce: "replayed_nonce",
|
|
160
|
+
bodyTooLarge: "body_too_large",
|
|
161
|
+
environmentMismatch: "environment_mismatch",
|
|
162
|
+
missingToken: "missing_token",
|
|
163
|
+
invalidToken: "invalid_token"
|
|
164
|
+
};
|
|
165
|
+
var RUNTIME_METERING_CONTRACT = {
|
|
166
|
+
endpoint: "/v1/metering/events",
|
|
167
|
+
method: "POST",
|
|
168
|
+
credential: "reusable-bearer",
|
|
169
|
+
event: {
|
|
170
|
+
event_id: "string",
|
|
171
|
+
product_id: "string",
|
|
172
|
+
backend_id: "string",
|
|
173
|
+
route_id: "string?",
|
|
174
|
+
request_id: "string?",
|
|
175
|
+
meter: "string",
|
|
176
|
+
qty: "number",
|
|
177
|
+
timestamp: "string"
|
|
178
|
+
},
|
|
179
|
+
idempotencyKey: "event_id",
|
|
180
|
+
delivery: "at-least-once",
|
|
181
|
+
billingOnly: true,
|
|
182
|
+
realtimeEnforced: false,
|
|
183
|
+
trustModel: "backend-reported values are NOT cryptographically attested; a buggy or compromised backend can self-report arbitrary values for its OWN product only. Core enforces allowedMeters/allowedRoutes from the authoritative token record at ingest, applies a per-event sanity max (perEventMax), and raises an implausible-volume alert."
|
|
184
|
+
};
|
|
185
|
+
var RUNTIME_HEALTH_CONTRACT = {
|
|
186
|
+
endpoint: "/v1/runtime/health",
|
|
187
|
+
method: "POST",
|
|
188
|
+
request: {
|
|
189
|
+
instanceId: "string?",
|
|
190
|
+
status: "starting | ready | degraded | stopping"
|
|
191
|
+
},
|
|
192
|
+
readinessStates: ["UNKNOWN", "WAITING", "READY", "DEGRADED", "OFFLINE"],
|
|
193
|
+
checks: [
|
|
194
|
+
"runtime_token_valid",
|
|
195
|
+
"bootstrapped",
|
|
196
|
+
"tunnel_running",
|
|
197
|
+
"signed_request_2xx",
|
|
198
|
+
"unsigned_request_401",
|
|
199
|
+
"stale_signature_401",
|
|
200
|
+
"wrong_route_401",
|
|
201
|
+
"metering_observed"
|
|
202
|
+
],
|
|
203
|
+
report: {
|
|
204
|
+
runtimeToken: "boolean",
|
|
205
|
+
bootstrap: "boolean",
|
|
206
|
+
tunnel: "string?",
|
|
207
|
+
verification: "boolean",
|
|
208
|
+
metering: "boolean"
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
var RUNTIME_TRANSPORT_CONTRACT = {
|
|
212
|
+
modes: {
|
|
213
|
+
public_origin: "Gateway fetches the builder's public origin URL; the SDK middleware fail-closed-verifies every request. Provisions zero Cloudflare objects. Standard tier; also the dev path.",
|
|
214
|
+
mtls: "Gateway presents a Farther-Shore-issued platform-wide client cert; the builder's ingress requires + verifies it. Channel-level mutual auth; provisions zero Cloudflare objects. Standard-plus / self-hosted tier.",
|
|
215
|
+
cloudflare_tunnel: "Farther Shore provisions a private outbound Cloudflare Tunnel; no inbound port. The only Production-secure tier. Consumes Cloudflare tunnel/route slots."
|
|
216
|
+
},
|
|
217
|
+
runners: {
|
|
218
|
+
managed_cloudflared: "fs.start() supervises cloudflared as a child process (default DX).",
|
|
219
|
+
sidecar: "Vanilla cloudflare/cloudflared container beside the app (production / non-Node)."
|
|
220
|
+
},
|
|
221
|
+
channelTrust: ["mtls", "cloudflare_tunnel"],
|
|
222
|
+
requestTrust: "x-fs-signature",
|
|
223
|
+
invariant: "Channel trust (mtls/tunnel) and request trust (the X-FS-* signature) are distinct layers; both always apply. CF-Access-* headers are transport-layer only and are IGNORED by the SDK."
|
|
224
|
+
};
|
|
225
|
+
export {
|
|
226
|
+
RUNTIME_BODY_HASH_CONTRACT,
|
|
227
|
+
RUNTIME_BOOTSTRAP_CONTRACT,
|
|
228
|
+
RUNTIME_CANONICAL_FIELDS,
|
|
229
|
+
RUNTIME_CONTRACT_VERSION,
|
|
230
|
+
RUNTIME_ERROR_CODES,
|
|
231
|
+
RUNTIME_HEADERS,
|
|
232
|
+
RUNTIME_HEALTH_CONTRACT,
|
|
233
|
+
RUNTIME_METERING_CONTRACT,
|
|
234
|
+
RUNTIME_REPLAY_CONTRACT,
|
|
235
|
+
RUNTIME_SIGNING_CONTRACT,
|
|
236
|
+
RUNTIME_TOKEN_CONTRACT,
|
|
237
|
+
RUNTIME_TOKEN_ENV,
|
|
238
|
+
RUNTIME_TRANSPORT_CONTRACT
|
|
239
|
+
};
|