peer-kit / src / ardegazu / peer / node / identity.cljs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
;; ported-from: src/node/identity.ts @ v1.3.0
;;
;; Suite identity for a headless peer — the Node counterpart of the games'
;; id/boot.ts. No iframe bridge: the 43-char base64url seed IS the identity,
;; held wherever the host process keeps secrets (file by default). A browser can
;; adopt the same identity by importing the seed on the hub.
;;
;; Vendor retirement: `Identity` and the Profile shape now come from the real
;; ardegazu-id-kit instead of src/vendor/id — compiled from its CLJS sources on
;; this build's classpath (see deps.edn), so the whole build shares exactly ONE
;; Identity class and `instanceof` holds everywhere.
(ns ardegazu.peer.node.identity
  (:require [ardegazu.id.identity :as id-identity]
            ["node:fs" :refer (mkdirSync readFileSync writeFileSync)]
            ["node:path" :refer (dirname)]
            [shadow.cljs.modern :refer (js-await)]))

(def ^:private SEED-RE #"^[A-Za-z0-9_-]{43}$")

(defn load-identity
  "opts = #js {:seedFile? :seed? :name? :hue? :glyph?}
   Resolves to #js {:identity :seed :profile :netIdentity}."
  [opts]
  (-> (js/Promise.resolve nil)
      (.then
       (fn [_]
         (let [explicit (let [s (unchecked-get opts "seed")]
                          (if ^boolean (js* "~{} == null" s) nil s))
               seed-file (unchecked-get opts "seedFile")]
           (when (and (some? explicit) (not (.test SEED-RE explicit)))
             (throw (js/Error. "invalid suite seed (want 43 chars of base64url)")))
           (let [seed
                 (if (or (some? explicit) (not ^boolean (js* "!!(~{})" seed-file)))
                   explicit
                   (try
                     (let [read (.trim (readFileSync seed-file "utf8"))]
                       (when-not (.test SEED-RE read)
                         (throw (js/Error. (str "seed file " seed-file
                                                " is not a valid suite seed"))))
                       read)
                     (catch :default err
                       (if-not (identical? "ENOENT" (unchecked-get err "code"))
                         (throw err)
                         (let [fresh (id-identity/new-seed)]
                           (mkdirSync (dirname seed-file) (js-obj "recursive" true))
                           (writeFileSync seed-file (str fresh "\n") (js-obj "mode" 0x180)) ; 0o600
                           fresh)))))]
             (when-not ^boolean (js* "!!(~{})" seed)
               (throw (js/Error. "no seed: pass seed or seedFile")))
             (js-await [identity (id-identity/from-seed-impl seed)]
               (let [hue (unchecked-get opts "hue")
                     glyph (unchecked-get opts "glyph")
                     nm (unchecked-get opts "name")
                     profile (js-obj "name" (if ^boolean (js* "~{} == null" nm) "" nm)
                                     "hue" (if (identical? "number" (js* "typeof ~{}" hue)) hue nil)
                                     "glyph" (if ^boolean (js* "~{} == null" glyph) nil glyph)
                                     "lang" nil)]
                 (js-obj
                  "identity" identity
                  "seed" seed
                  "profile" profile
                  ;; Ready-to-pass identity block for createNet (per-app salt).
                  "netIdentity"
                  (fn [app-salt]
                    (js-obj "pub" (unchecked-get identity "publicKeyB64")
                            "sign" (fn [room-id my-peer-id]
                                     (.assert ^js identity app-salt room-id my-peer-id))
                            ;; `profile.name || undefined`
                            "name" (let [n (unchecked-get profile "name")]
                                     (if ^boolean (js* "!!(~{})" n) n js/undefined))
                            "hue" (unchecked-get profile "hue")
                            "glyph" (unchecked-get profile "glyph"))))))))))))

static mirror of HEAD · about · clone: git clone https://git.ardegazu.ro/peer-kit.git