@le-space/rootfs 0.6.11 → 0.6.19

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@le-space/rootfs",
3
- "version": "0.6.11",
3
+ "version": "0.6.19",
4
4
  "description": "Shared rootfs contract parsing, reference profile assets, and build helpers.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -168,7 +168,8 @@ def json_dumps(payload: object) -> str:
168
168
  def sign_personal_message(private_key: str, payload: str) -> str:
169
169
  message = encode_defunct(text=payload)
170
170
  signed = Account.sign_message(message, private_key=private_key)
171
- return signed.signature.hex()
171
+ signature = signed.signature.hex()
172
+ return signature if signature.startswith("0x") else f"0x{signature}"
172
173
 
173
174
 
174
175
  def address_from_private_key(private_key: str) -> str:
@@ -179,6 +180,21 @@ def signature_payload(chain: str, sender: str, message_type: str, item_hash: str
179
180
  return "\n".join([chain, sender, message_type, item_hash])
180
181
 
181
182
 
183
+ def registration_id_instance_item_hash(registration_id: str | None) -> str | None:
184
+ normalized = str(registration_id or "").strip()
185
+ if not normalized:
186
+ return None
187
+
188
+ parts = [part.strip() for part in normalized.split(":") if part.strip()]
189
+ if not parts:
190
+ return None
191
+
192
+ candidate = parts[-1]
193
+ if len(candidate) == 64 and all(char in "0123456789abcdefABCDEF" for char in candidate):
194
+ return candidate
195
+ return None
196
+
197
+
182
198
  def relay_authorization_payload(
183
199
  owner_address: str,
184
200
  publisher_address: str,
@@ -186,6 +202,7 @@ def relay_authorization_payload(
186
202
  registration_id: str | None,
187
203
  profile: str | None,
188
204
  version: str | None,
205
+ instance_item_hash: str | None,
189
206
  issued_at: int,
190
207
  ) -> dict[str, object]:
191
208
  return {
@@ -195,7 +212,7 @@ def relay_authorization_payload(
195
212
  "registrationId": registration_id,
196
213
  "profile": profile,
197
214
  "version": version,
198
- "instanceItemHash": None,
215
+ "instanceItemHash": instance_item_hash,
199
216
  "issuedAt": issued_at,
200
217
  "expiresAt": None,
201
218
  }
@@ -244,6 +261,7 @@ def load_owner_authorization(
244
261
  return None
245
262
 
246
263
  owner_address = address_from_private_key(owner_private_key)
264
+ instance_item_hash = registration_id_instance_item_hash(registration_id)
247
265
  payload = relay_authorization_payload(
248
266
  owner_address,
249
267
  publisher_address,
@@ -251,6 +269,7 @@ def load_owner_authorization(
251
269
  registration_id,
252
270
  profile,
253
271
  version,
272
+ instance_item_hash,
254
273
  issued_at,
255
274
  )
256
275
  return {
@@ -260,6 +279,28 @@ def load_owner_authorization(
260
279
  }
261
280
 
262
281
 
282
+ def resolve_owner_address(
283
+ env_values: dict[str, str],
284
+ owner_authorization: dict[str, object] | None,
285
+ ) -> str | None:
286
+ if isinstance(owner_authorization, dict):
287
+ payload = owner_authorization.get("payload")
288
+ if isinstance(payload, dict):
289
+ owner_address = str(payload.get("ownerAddress") or "").strip()
290
+ if owner_address:
291
+ return owner_address
292
+
293
+ owner_address = env_values.get("ALEPH_BOOTSTRAP_OWNER_ADDRESS", "").strip()
294
+ if owner_address:
295
+ return owner_address
296
+
297
+ owner_private_key = env_values.get("ALEPH_BOOTSTRAP_OWNER_PRIVATE_KEY", "").strip()
298
+ if owner_private_key:
299
+ return address_from_private_key(owner_private_key)
300
+
301
+ return None
302
+
303
+
263
304
  def post_json(url: str, body: dict[str, object]) -> tuple[int, dict]:
264
305
  data = json_dumps(body).encode("utf-8")
265
306
  request = urllib.request.Request(
@@ -293,9 +334,11 @@ def build_post_content(
293
334
  registration_id: str | None,
294
335
  profile: str | None,
295
336
  version: str | None,
337
+ owner_address: str | None,
296
338
  owner_authorization: dict[str, object] | None,
297
339
  relay_proof: dict[str, object],
298
340
  now_ms: int,
341
+ now_seconds: float,
299
342
  ref: str,
300
343
  post_type: str,
301
344
  ) -> dict[str, object]:
@@ -306,22 +349,20 @@ def build_post_content(
306
349
  "registrationId": registration_id,
307
350
  "profile": profile,
308
351
  "version": version,
309
- "ownerAddress": (
310
- owner_authorization.get("payload", {}).get("ownerAddress")
311
- if isinstance(owner_authorization, dict)
312
- else None
313
- ),
352
+ "ownerAddress": owner_address,
314
353
  "publisherAddress": sender,
315
354
  "authorization": owner_authorization,
316
355
  "relayProof": relay_proof,
317
356
  "updatedAt": now_ms,
318
357
  }
358
+ if browser_multiaddrs:
359
+ content["browserMultiaddrs"] = browser_multiaddrs
319
360
  return {
320
361
  "type": post_type,
321
362
  "address": sender,
322
363
  "ref": ref,
323
364
  "content": content,
324
- "time": now_ms,
365
+ "time": now_seconds,
325
366
  }
326
367
 
327
368
 
@@ -550,9 +591,7 @@ def main() -> None:
550
591
  version,
551
592
  now_ms,
552
593
  )
553
- if owner_authorization is None:
554
- print(json_dumps({"status": "skipped", "reason": "missing owner authorization"}))
555
- return
594
+ owner_address = resolve_owner_address(env_values, owner_authorization)
556
595
 
557
596
  proof_payload = relay_proof_payload(
558
597
  peer_id,
@@ -576,9 +615,11 @@ def main() -> None:
576
615
  registration_id,
577
616
  profile,
578
617
  version,
618
+ owner_address,
579
619
  owner_authorization,
580
620
  relay_proof,
581
621
  now_ms,
622
+ now_seconds,
582
623
  ref,
583
624
  post_type,
584
625
  )
@@ -614,6 +655,7 @@ def main() -> None:
614
655
  "publishedBrowserMultiaddrs": published_browser_multiaddrs or published_multiaddrs,
615
656
  "forgottenHashes": previous_hashes,
616
657
  "forgetStaleOlderThanSeconds": STALE_RECORD_MAX_AGE_SECONDS,
658
+ "ownerAuthorizationPresent": owner_authorization is not None,
617
659
  "forgetResponse": forget_response[1] if forget_response else None,
618
660
  "response": response,
619
661
  }
@@ -118,6 +118,5 @@ write_env_var "RELAY_WEBRTC_PORT" "9093"
118
118
  write_env_var "RELAY_QUIC_PORT" "9094"
119
119
  write_env_var "RELAY_DISABLE_WEBRTC" "1"
120
120
  write_env_var "RELAY_DISABLE_BOOTSTRAP" "1"
121
- write_env_var "disableAutoTLS" "1"
122
121
  write_env_var "ENABLE_GENERAL_LOGS" "1"
123
122
  write_env_var "DEBUG" "'libp2p:websockets:listener'"
@@ -75,7 +75,13 @@ write_caddyfile() {
75
75
  auto_https disable_redirects
76
76
  }
77
77
 
78
- ${hostname} {
78
+ https://${hostname} {
79
+ tls {
80
+ issuer acme {
81
+ disable_tlsalpn_challenge
82
+ }
83
+ }
84
+
79
85
  # Proxy the full orbitdb-relay HTTP API surface to the metrics/HTTP port.
80
86
  # Everything else falls through to the relay WebSocket backend for browser libp2p.
81
87
  handle /health {
@@ -202,11 +208,11 @@ fi
202
208
  if [ -n "${PROXY_HOSTNAME}" ]; then
203
209
  announce+=("/dns4/${PROXY_HOSTNAME}/tcp/443/tls/ws")
204
210
  announce+=("/dns6/${PROXY_HOSTNAME}/tcp/443/tls/ws")
205
- else
206
- announce+=("/ip4/${PUBLIC_IPV4}/tcp/${WS_PORT}/ws")
207
- if [ -n "${PUBLIC_IPV6}" ]; then
208
- announce+=("/ip6/${PUBLIC_IPV6}/tcp/${WS_PORT}/ws")
209
- fi
211
+ fi
212
+
213
+ announce+=("/ip4/${PUBLIC_IPV4}/tcp/${WS_PORT}/ws")
214
+ if [ -n "${PUBLIC_IPV6}" ]; then
215
+ announce+=("/ip6/${PUBLIC_IPV6}/tcp/${WS_PORT}/ws")
210
216
  fi
211
217
 
212
218
  if [ -n "${WEBRTC_PORT}" ]; then
@@ -8,6 +8,7 @@ BOOTSTRAP_VALIDATOR="${BOOTSTRAP_VALIDATOR:-/usr/local/sbin/ucan_store_bootstrap
8
8
  BOOTSTRAP_CRYPTO_VERIFIER="${BOOTSTRAP_CRYPTO_VERIFIER:-/usr/local/sbin/ucan-store-bootstrap-verify.mjs}"
9
9
  BOOTSTRAP_VERIFICATION_FILE="${BOOTSTRAP_VERIFICATION_FILE:-/etc/ucan-store/bootstrap-verification.json}"
10
10
  SERVICE_NAME="${SERVICE_NAME:-ucan-store.service}"
11
+ BOOTSTRAP_SERVICE="${BOOTSTRAP_SERVICE:-ucan-store-bootstrap.service}"
11
12
  CADDY_SERVICE="${CADDY_SERVICE:-caddy.service}"
12
13
  CADDYFILE="${CADDYFILE:-/etc/caddy/Caddyfile}"
13
14
  SERVICE_PORT="${SERVICE_PORT:-8787}"
@@ -75,10 +76,19 @@ PY
75
76
  }
76
77
 
77
78
  write_caddyfile() {
79
+ local service_hostname="${1:-}"
80
+ local proxy_hostname="${2:-}"
81
+ local primary_hostname="${service_hostname}"
78
82
  local hostnames=()
79
83
  local hostname
80
84
  local known
81
- for hostname in "$@"; do
85
+
86
+ if [ -z "${primary_hostname}" ]; then
87
+ primary_hostname="${proxy_hostname}"
88
+ proxy_hostname=""
89
+ fi
90
+
91
+ for hostname in "${primary_hostname}" "${proxy_hostname}"; do
82
92
  if [ -z "${hostname}" ]; then
83
93
  continue
84
94
  fi
@@ -100,18 +110,45 @@ write_caddyfile() {
100
110
  return 0
101
111
  fi
102
112
 
103
- local site_label
113
+ local http_site_label=""
104
114
  local index
105
- site_label="${hostnames[0]}"
106
- for ((index = 1; index < ${#hostnames[@]}; index++)); do
107
- site_label+=", ${hostnames[${index}]}"
115
+ for ((index = 0; index < ${#hostnames[@]}; index++)); do
116
+ if [ -n "${http_site_label}" ]; then
117
+ http_site_label+=", "
118
+ fi
119
+ http_site_label+="http://${hostnames[${index}]}"
108
120
  done
109
121
  mkdir -p "$(dirname "${CADDYFILE}")"
110
122
  cat > "${CADDYFILE}" <<EOF
111
- ${site_label} {
123
+ {
124
+ auto_https disable_redirects
125
+ }
126
+
127
+ ${http_site_label} {
128
+ reverse_proxy 127.0.0.1:${PROXY_PORT}
129
+ }
130
+
131
+ https://${primary_hostname} {
132
+ tls {
133
+ issuer acme {
134
+ disable_tlsalpn_challenge
135
+ }
136
+ }
137
+
112
138
  reverse_proxy 127.0.0.1:${PROXY_PORT}
113
139
  }
114
140
  EOF
141
+
142
+ if [ -n "${proxy_hostname}" ] && [ "${proxy_hostname}" != "${primary_hostname}" ]; then
143
+ cat >> "${CADDYFILE}" <<EOF
144
+
145
+ https://${proxy_hostname} {
146
+ tls internal
147
+
148
+ reverse_proxy 127.0.0.1:${PROXY_PORT}
149
+ }
150
+ EOF
151
+ fi
115
152
  }
116
153
 
117
154
  derive_hostname_from_origin() {
@@ -295,15 +332,19 @@ if [ -n "${WEBAUTHN_ORIGIN}" ]; then
295
332
  fi
296
333
  write_env_var "WEBAUTHN_ORIGIN_FALLBACKS" "${WEBAUTHN_ORIGIN_FALLBACKS}"
297
334
 
298
- if [ -z "${SERVICE_DID}" ] && [ -n "${PROXY_HOSTNAME}" ]; then
299
- SERVICE_DID="$(derive_service_did_from_hostname "${PROXY_HOSTNAME}")"
300
- fi
301
-
302
335
  if [ -z "${SERVICE_ORIGIN}" ] && [ -n "${PROXY_HOSTNAME}" ]; then
303
336
  SERVICE_ORIGIN="https://${PROXY_HOSTNAME}"
304
337
  fi
305
338
  SERVICE_HOSTNAME="$(derive_hostname_from_origin "${SERVICE_ORIGIN}")"
306
339
 
340
+ if [ -z "${SERVICE_DID}" ]; then
341
+ if [ -n "${SERVICE_HOSTNAME}" ]; then
342
+ SERVICE_DID="$(derive_service_did_from_hostname "${SERVICE_HOSTNAME}")"
343
+ elif [ -n "${PROXY_HOSTNAME}" ]; then
344
+ SERVICE_DID="$(derive_service_did_from_hostname "${PROXY_HOSTNAME}")"
345
+ fi
346
+ fi
347
+
307
348
  write_env_var "UCAN_STORE_SERVICE_DID" "${SERVICE_DID}"
308
349
  write_env_var "UCAN_STORE_SERVICE_HOSTNAME" "${SERVICE_HOSTNAME}"
309
350
 
@@ -319,7 +360,7 @@ if [ -n "${SERVICE_ORIGIN}" ]; then
319
360
  fi
320
361
 
321
362
  if [ -n "${PROXY_HOSTNAME}" ]; then
322
- write_caddyfile "${PROXY_HOSTNAME}" "${SERVICE_HOSTNAME}"
363
+ write_caddyfile "${SERVICE_HOSTNAME:-}" "${PROXY_HOSTNAME}"
323
364
  fi
324
365
 
325
366
  touch "${READY_FILE}"
@@ -328,7 +369,12 @@ if [ "${START_SERVICE}" = "1" ]; then
328
369
  systemctl daemon-reload
329
370
  systemctl restart "${SERVICE_NAME}"
330
371
  if [ -n "${PROXY_HOSTNAME}" ]; then
331
- systemctl restart "${CADDY_SERVICE}"
372
+ systemctl enable "${CADDY_SERVICE}" >/dev/null 2>&1 || true
373
+ if systemctl is-active --quiet "${BOOTSTRAP_SERVICE}"; then
374
+ systemctl reset-failed "${CADDY_SERVICE}" >/dev/null 2>&1 || true
375
+ else
376
+ systemctl restart "${CADDY_SERVICE}"
377
+ fi
332
378
  fi
333
379
 
334
380
  PROBED_SERVICE_DID="$(probe_service_did)"
@@ -12,6 +12,8 @@ ENV_FILE = os.environ.get("ENV_FILE", "/etc/default/ucan-store")
12
12
  READY_FILE = os.environ.get("READY_FILE", "/etc/default/ucan-store.ready")
13
13
  SERVICE_NAME = os.environ.get("SERVICE_NAME", "ucan-store.service")
14
14
  BOOTSTRAP_SERVICE = os.environ.get("BOOTSTRAP_SERVICE", "ucan-store-bootstrap.service")
15
+ CADDY_SERVICE = os.environ.get("CADDY_SERVICE", "caddy.service")
16
+ CADDYFILE = os.environ.get("CADDYFILE", "/etc/caddy/Caddyfile")
15
17
  CONFIGURE_SCRIPT = "/usr/local/sbin/ucan-store-configure.sh"
16
18
  DESCRIBE_SCRIPT = "/usr/local/sbin/ucan-store-describe.py"
17
19
  METADATA_FILE = os.environ.get("METADATA_FILE", "/run/ucan-store-setup-metadata.json")
@@ -49,6 +51,18 @@ def _generate_metadata_files() -> None:
49
51
 
50
52
 
51
53
  def _stop_bootstrap_service() -> None:
54
+ if os.path.exists(CADDYFILE):
55
+ subprocess.run(
56
+ [
57
+ "systemd-run",
58
+ "--unit=ucan-store-caddy-after-bootstrap",
59
+ "--on-active=3",
60
+ "/bin/systemctl",
61
+ "restart",
62
+ CADDY_SERVICE,
63
+ ],
64
+ check=False,
65
+ )
52
66
  subprocess.run(["systemctl", "stop", BOOTSTRAP_SERVICE], check=False)
53
67
 
54
68