@ethernity-cloud/sdk-js 1.1.1 → 1.4.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/build.js CHANGED
@@ -1,7 +1,36 @@
1
1
  const { execSync } = require('child_process');
2
+ const fs = require('fs');
2
3
  const path = require('path');
3
4
  require('dotenv').config();
4
5
 
6
+ // Increment VERSION on every build, mirroring the Python SDK's build.py
7
+ // (config VERSION += 1). VERSION is the immutable per-build registry channel and
8
+ // the enclave-name suffix, so each build must advance it. Existing projects seed
9
+ // VERSION as "v1"; parse whatever trailing integer is present (v1 -> 1, 22 -> 22,
10
+ // empty -> 0), increment, and persist as a plain integer. It must never equal
11
+ // "v3" (the protocol channel that acts as the moving "latest" pointer); plain
12
+ // integers can't collide with the "v3" string, so this is safe.
13
+ const writeEnv = (key, value) => {
14
+ const envFile = '.env';
15
+ let content = fs.existsSync(envFile) ? fs.readFileSync(envFile, 'utf8') : '';
16
+ const regex = new RegExp(`^${key}=.*`, 'm');
17
+ if (regex.test(content)) content = content.replace(regex, `${key}=${value}`);
18
+ else content += (content && !content.endsWith('\n') ? '\n' : '') + `${key}=${value}`;
19
+ fs.writeFileSync(envFile, content, 'utf8');
20
+ };
21
+
22
+ const bumpVersion = () => {
23
+ const raw = (process.env.VERSION || '0').toString();
24
+ const digits = raw.match(/\d+/);
25
+ const next = (digits ? parseInt(digits[0], 10) : 0) + 1;
26
+ process.env.VERSION = String(next);
27
+ writeEnv('VERSION', String(next));
28
+ console.log(`Version incremented to ${next}`);
29
+ return next;
30
+ };
31
+
32
+ bumpVersion();
33
+
5
34
  const serviceType = process.env.SERVICE_TYPE;
6
35
 
7
36
  if (serviceType === "Nodenithy") {
package/init.js CHANGED
@@ -204,7 +204,7 @@ const main = async () => {
204
204
  ),
205
205
  );
206
206
  } else {
207
- customUrl = "http://ipfs.ethernity.cloud:5001";
207
+ customUrl = "https://ipfs.ethernity.cloud";
208
208
  }
209
209
 
210
210
  fs.mkdirSync("src/serverless", { recursive: true });
@@ -6,7 +6,6 @@ RUN cd /
6
6
 
7
7
  RUN apk add bash
8
8
 
9
- ENV SCONE_MODE=SIM
10
9
  ENV SCONE_HEAP=256M
11
10
  ENV SCONE_LOG=DEBUG
12
11
  ENV ENCLAVE_NAME_SECURELOCK=__ENCLAVE_NAME_SECURELOCK__
@@ -50,14 +49,31 @@ RUN apk add openssl
50
49
 
51
50
  RUN openssl genrsa -3 -out /enclave-key.pem 3072
52
51
 
53
-
54
- ENV SCONE_LOG=info
52
+ # SCONE runtime env. These MUST match the enclave-creation params passed to
53
+ # scone-signer below (SCONE embeds defaults for anything not signed explicitly;
54
+ # any drift makes SCONE recompute + debug re-sign the enclave at load, which CAS
55
+ # rejects on mainnet). Values mirror the publish/run docker-compose securelock
56
+ # service (SCONE_HEAP=1024M, SCONE_STACK=4M, dlopen=1, extensions=libbinary-fs).
55
57
  ENV SCONE_HEAP=1024M
58
+ ENV SCONE_STACK=4M
56
59
  ENV SCONE_ALLOW_DLOPEN=1
57
60
  ENV SCONE_EXTENSIONS_PATH=/lib/libbinary-fs.so
58
61
 
59
- # Enable production mode for mainnet
60
- # RUN scone-signer sign --key=/enclave-key.pem --env --production /usr/bin/node
62
+ # Rendered by build.mjs. Signs the EXECUTED binary (/usr/local/bin/node -- the
63
+ # ENTRYPOINT and the run/publish compose command both run /usr/local/bin/node).
64
+ # Mainnet adds --production (CAS/DCAP attestation); testnet omits it (non-CAS
65
+ # self-sign). On testnet builds this line is a no-op comment.
66
+ __SCONE_SIGN__
67
+
68
+ # Record the SIGNED MRENCLAVE so publish (run.js) can assert the runtime enclave
69
+ # measurement matches what was signed BEFORE registering the CAS session /
70
+ # publishing. A mismatch means SCONE recomputed the enclave at load (params
71
+ # drift) => debug re-sign => CAS rejects the DCAP quote on mainnet. Only produced
72
+ # for --production (mainnet) signs; on testnet the file is absent and the check
73
+ # is skipped. Capture to a file first: piping scone-signer into `head` kills it
74
+ # with SIGPIPE (exit 141) under buildkit's pipefail shell.
75
+ __SIGNED_MRENCLAVE__
76
+
61
77
  RUN rm -rf /enclave-key.pem
62
78
 
63
79
  #RUN scone cas attest scone-cas.cf 3061b9feb7fa67f3815336a085f629a13f04b0a1667c93b14ff35581dc8271e4 -GCS --only_for_testing-debug --only_for_testing-ignore-signer
@@ -0,0 +1,41 @@
1
+ # SGX key-gen module (`sgx_report.node`)
2
+
3
+ The JS SDK ships this module **only as a prebuilt N-API addon**
4
+ (`sgx_report.node`) — never the C source (`get_sgx_report.c`,
5
+ `sgx_report_napi.c`). The identity-key derivation (SGX EREPORT + obfuscated
6
+ MR_SIGNER + HKDF-SHA512 domain-separated derivation) lives entirely in the
7
+ compiled binary, so a developer inspecting the package or build tree never sees
8
+ the algorithm. `get_sgx_report.c` and `sgx_report_napi.c` are gitignored here
9
+ so they can never be committed.
10
+
11
+ ## Where the source lives
12
+
13
+ The source of truth is the runtime repo, not this package:
14
+
15
+ - `etny-nodenithy` → `v3/build/{securelock,trustedzone,validator}/src/`
16
+ (`get_sgx_report.c` + `sgx_report_napi.c`)
17
+
18
+ ## How the `.node` is produced
19
+
20
+ `sgx_report.node` is a **build artifact of the etny-nodenithy pipeline** — its
21
+ Dockerfile compiles `sgx_report_napi.c` (which `#include`s `get_sgx_report.c`)
22
+ into the addon with a single gcc command against node's `node_api.h`. To refresh
23
+ the copy the SDK ships, take the `sgx_report.node` that pipeline produces and
24
+ commit it here as `securelock/src/sgx_report.node`.
25
+
26
+ The JS wrappers `sgx_report.js` (loader) and `self_sign.js` (calls
27
+ `deriveIdentityScalar`) ship as source — they contain no algorithm, only calls
28
+ into the addon.
29
+
30
+ ## Why not compile it in the SDK build
31
+
32
+ The securelock Dockerfile bakes `src/` into binary-fs but does **not** compile
33
+ the addon, so the SDK must carry the prebuilt `.node`. `build.mjs` fails fast if
34
+ `src/sgx_report.node` is missing.
35
+
36
+ ## Honest scope
37
+
38
+ This hides the SOURCE, not the algorithm: the `.node` still contains the
39
+ executable logic and the enclave image is public. On testnet (no attestation)
40
+ the derived key is reproducible by anyone who runs the image. Key exclusivity
41
+ comes from attestation, not from shipping only the binary.
@@ -2,34 +2,62 @@ const fs = require('fs');
2
2
  const path = require('path');
3
3
  const { TaskStatus } = require('./task_status');
4
4
 
5
- // Load all JavaScript files from the ./serverless/ folder
6
- // const serverlessDir = path.join(__dirname, './serverless');
7
-
8
- // let backendFunctions = {};
9
- // fs.readdirSync(serverlessDir).forEach(file => {
10
- // if (file.endsWith('.js')) {
11
- // const filePath = path.join(serverlessDir, file);
12
- // const moduleExports = require(filePath.replace('.js', ''));
13
- // backendFunctions = { ...backendFunctions, ...moduleExports };
14
- // }
15
- // });
16
- const { hello } = require('./serverless/backend');
5
+ // Load EVERY export of ./serverless/backend into the payload's scope. The
6
+ // backend is copied next to this file by ecld-build (and staged the same way
7
+ // by ecld-test locally), so one code path serves both the enclave and local
8
+ // testing. Previously only a function literally named `hello` was reachable
9
+ // (hardcoded destructure); any other exported function threw ReferenceError.
10
+ // A missing backend leaves only ___etny_result___ in scope, which is the
11
+ // stock-enclave behaviour.
12
+ let backendFunctions = {};
13
+ // If the backend fails to load, remember WHY. A silently-swallowed failure
14
+ // (missing npm module, bad require inside backend.js) makes every task die
15
+ // later with a misleading "X is not defined". Only a genuinely absent backend
16
+ // stays silent — that is the stock-enclave configuration.
17
+ let backendImportError = null;
18
+ try {
19
+ backendFunctions = { ...require('./serverless/backend') };
20
+ } catch (e) {
21
+ backendFunctions = {};
22
+ // MODULE_NOT_FOUND messages embed the whole require stack (which always
23
+ // mentions serverless/backend.js), so parse out WHICH module is missing:
24
+ // only a missing backend module itself means "no backend shipped".
25
+ const m = /Cannot find module '([^']+)'/.exec(e.message || '');
26
+ const missingBackendItself =
27
+ e.code === 'MODULE_NOT_FOUND' && m && /serverless[\\/]backend/.test(m[1]);
28
+ if (!missingBackendItself) {
29
+ backendImportError = `${e.name || 'Error'}: ${(e.message || '').split('\n')[0]}`;
30
+ }
31
+ }
17
32
 
18
33
  function ___etny_result___(data) {
19
34
  return [0, data];
20
35
  }
36
+
21
37
  function executeTask(payload, input) {
22
- return exec(payload, input, { '___etny_result___': ___etny_result___, 'hello': hello });
38
+ if (backendImportError !== null) {
39
+ return [
40
+ TaskStatus.IMPORT_ERROR,
41
+ 'BACKEND IMPORT ERROR: ' + backendImportError +
42
+ ' | The serverless backend failed to load inside the enclave, so none of its' +
43
+ ' functions are available. Common causes: a module missing from the enclave' +
44
+ ' image dependencies, or a bad require() inside backend.js.'
45
+ ];
46
+ }
47
+ return exec(payload, input, { '___etny_result___': ___etny_result___, ...backendFunctions });
23
48
  }
49
+
24
50
  function exec(payload, input, globals = null) {
25
51
  try {
26
52
  if (payload && payload !== "") {
53
+ const scope = globals || {};
27
54
  if (input && input !== "") {
28
- if (globals) {
29
- globals['___etny_data_set___'] = input;
30
- }
31
- return ___etny_result___(eval(payload));
32
- } else {
55
+ scope['___etny_data_set___'] = input;
56
+ }
57
+ // `with` exposes every backend function and ___etny_data_set___ to
58
+ // the payload by bare name — matching how payloads are written
59
+ // (e.g. `processData(___etny_data_set___)`).
60
+ with (scope) {
33
61
  return ___etny_result___(eval(payload));
34
62
  }
35
63
  } else {