@umbra-privacy/ceremony 0.1.2 → 0.2.3

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.
Files changed (2) hide show
  1. package/dist/index.js +196 -87
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import { render } from "ink";
5
5
 
6
6
  // src/components/App.tsx
7
7
  import { useEffect as useEffect4, useState as useState4 } from "react";
8
- import { Box as Box6, Text as Text6, useApp, useInput as useInput2 } from "ink";
8
+ import { Box as Box7, Text as Text7, useApp, useInput as useInput2 } from "ink";
9
9
 
10
10
  // src/cleanup.ts
11
11
  var pendingLeaveQueue = null;
@@ -33,7 +33,7 @@ import { createWriteStream } from "fs";
33
33
  import { readFile, unlink } from "fs/promises";
34
34
  import { pipeline } from "stream/promises";
35
35
  import { Readable } from "stream";
36
- var DEFAULT_API_URL = "http://ceremony.api.umbraprivacy.com";
36
+ var DEFAULT_API_URL = "https://ceremony.api.umbraprivacy.com";
37
37
  var BASE = (process.env["CEREMONY_API_URL"] ?? DEFAULT_API_URL).replace(/\/$/, "");
38
38
  async function request(path, options = {}) {
39
39
  const res = await fetch(`${BASE}${path}`, {
@@ -262,7 +262,7 @@ var STATUS_COLOR = {
262
262
  function Header({ ceremony, subtitle }) {
263
263
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
264
264
  /* @__PURE__ */ jsxs(Box, { children: [
265
- /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "\u25C6 CEREMONY TUI" }),
265
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "\u25C6 Umbra Ceremony TUI" }),
266
266
  ceremony && /* @__PURE__ */ jsxs(Fragment, { children: [
267
267
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
268
268
  /* @__PURE__ */ jsx(Text, { bold: true, children: ceremony.name }),
@@ -271,6 +271,9 @@ function Header({ ceremony, subtitle }) {
271
271
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "]" })
272
272
  ] })
273
273
  ] }),
274
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: "Phase 2 trusted-setup contribution \xB7 Groth16 / BN254" }),
275
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: "You are adding fresh entropy to Umbra's circuit proving keys. The on-chain privacy of every deposit, claim and transfer holds as long as ONE contributor in this ceremony destroys their secret." }),
276
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Press ? at any time for a full explanation of what is happening." }),
274
277
  ceremony && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
275
278
  ceremony.total_tracks,
276
279
  " circuit",
@@ -688,26 +691,56 @@ function Attestation({ contribution }) {
688
691
  ] });
689
692
  }
690
693
 
691
- // src/components/App.tsx
694
+ // src/components/InfoModal.tsx
695
+ import { Box as Box6, Text as Text6 } from "ink";
692
696
  import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
693
- function App({ ceremonyId: initialCeremonyId, displayName: displayName2 = "anonymous" }) {
697
+ function InfoModal() {
698
+ return /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", paddingX: 2, paddingY: 1, children: /* @__PURE__ */ jsxs6(
699
+ Box6,
700
+ {
701
+ flexDirection: "column",
702
+ borderStyle: "round",
703
+ borderColor: "cyan",
704
+ paddingX: 2,
705
+ paddingY: 1,
706
+ width: 84,
707
+ gap: 1,
708
+ children: [
709
+ /* @__PURE__ */ jsx6(Text6, { bold: true, color: "cyan", children: "Umbra Phase 2 Trusted-Setup Ceremony" }),
710
+ /* @__PURE__ */ jsx6(Text6, { children: "Umbra uses Groth16 zero-knowledge proofs to give Solana users on-chain privacy. Every circuit needs a one-time multi-party ceremony to generate its proving key safely \u2014 that is Phase 2. Each contributor adds their own secret entropy and destroys it afterward. The setup stays secure as long as AT LEAST ONE contributor erased theirs, which is why your single contribution genuinely matters." }),
711
+ /* @__PURE__ */ jsx6(Text6, { children: "What you will do: tap 20 keys to seed your entropy, download the latest challenge file, run snarkjs locally to combine your secret with the parameters, and upload the response. Your secret never leaves your machine. Erase it when you are done." }),
712
+ /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Press B, Esc or ? to close" })
713
+ ]
714
+ }
715
+ ) });
716
+ }
717
+
718
+ // src/components/App.tsx
719
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
720
+ var NAME_MAX_LEN = 100;
721
+ var NAME_VALID_RE = /^[\p{L}\p{N} _.\-]*$/u;
722
+ function App({ ceremonyId: initialCeremonyId, displayName: initialDisplayName }) {
694
723
  const { exit } = useApp();
695
724
  const [activeCeremonyId, setActiveCeremonyId] = useState4(initialCeremonyId);
725
+ const [displayName2, setDisplayName] = useState4(initialDisplayName ?? "anonymous");
726
+ const [nameSet, setNameSet] = useState4(initialDisplayName !== void 0);
696
727
  const [screen, setScreen] = useState4(
697
- initialCeremonyId ? { name: "loading" } : { name: "ceremony-picker", ceremonies: [], loading: true }
728
+ initialDisplayName === void 0 ? { name: "name-input", value: "" } : initialCeremonyId ? { name: "loading" } : { name: "ceremony-picker", ceremonies: [], loading: true }
698
729
  );
699
730
  const [ceremony, setCeremony] = useState4(null);
700
731
  const [session, setSession] = useState4(null);
701
732
  const [contributed, setContributed] = useState4({});
702
733
  const [selectedIdx, setSelectedIdx] = useState4(0);
703
734
  const [tab, setTab] = useState4(0);
735
+ const [showInfo, setShowInfo] = useState4(false);
704
736
  useEffect4(() => {
737
+ if (!nameSet) return;
705
738
  if (!initialCeremonyId) {
706
739
  loadCeremonies();
707
740
  } else {
708
741
  boot(initialCeremonyId);
709
742
  }
710
- }, []);
743
+ }, [nameSet]);
711
744
  async function loadCeremonies() {
712
745
  try {
713
746
  const { ceremonies } = await api.listCeremonies();
@@ -739,6 +772,7 @@ function App({ ceremonyId: initialCeremonyId, displayName: displayName2 = "anony
739
772
  setScreen({ name: "error", message: "No tracks found in this ceremony.", recoverable: false });
740
773
  return;
741
774
  }
775
+ setSelectedIdx(0);
742
776
  setScreen({ name: "tracks", tracks });
743
777
  } catch (e) {
744
778
  if (e.code === "INVALID_SESSION" || e.status === 401) {
@@ -751,6 +785,7 @@ function App({ ceremonyId: initialCeremonyId, displayName: displayName2 = "anony
751
785
  setScreen({ name: "error", message: "No tracks found in this ceremony.", recoverable: false });
752
786
  return;
753
787
  }
788
+ setSelectedIdx(0);
754
789
  setScreen({ name: "tracks", tracks });
755
790
  } catch (e2) {
756
791
  setScreen({ name: "error", message: e2.message ?? "Failed to load tracks.", recoverable: false });
@@ -760,6 +795,22 @@ function App({ ceremonyId: initialCeremonyId, displayName: displayName2 = "anony
760
795
  setScreen({ name: "error", message: e.message ?? "Failed to load tracks.", recoverable: false });
761
796
  }
762
797
  }
798
+ function commitName(raw) {
799
+ const trimmed = raw.trim();
800
+ if (trimmed.length === 0) return;
801
+ setDisplayName(trimmed);
802
+ setNameSet(true);
803
+ setScreen(
804
+ initialCeremonyId ? { name: "loading" } : { name: "ceremony-picker", ceremonies: [], loading: true }
805
+ );
806
+ }
807
+ function randomAnonName() {
808
+ const hex = Array.from(
809
+ { length: 6 },
810
+ () => Math.floor(Math.random() * 16).toString(16)
811
+ ).join("");
812
+ return `anon-${hex}`;
813
+ }
763
814
  async function joinTrack(track) {
764
815
  if (!session) return;
765
816
  setScreen({ name: "joining" });
@@ -795,6 +846,44 @@ function App({ ceremonyId: initialCeremonyId, displayName: displayName2 = "anony
795
846
  }, [screen.name]);
796
847
  useInput2((input, key) => {
797
848
  const q = input.toLowerCase();
849
+ if (showInfo) {
850
+ if (key.escape || input === "?" || q === "b") {
851
+ setShowInfo(false);
852
+ return;
853
+ }
854
+ if (q === "q") {
855
+ exit();
856
+ return;
857
+ }
858
+ return;
859
+ }
860
+ if (input === "?" && screen.name !== "entropy") {
861
+ setShowInfo(true);
862
+ return;
863
+ }
864
+ if (screen.name === "name-input") {
865
+ if (key.escape) {
866
+ exit();
867
+ return;
868
+ }
869
+ if (key.tab) {
870
+ setScreen({ name: "name-input", value: randomAnonName() });
871
+ return;
872
+ }
873
+ if (key.return) {
874
+ commitName(screen.value);
875
+ return;
876
+ }
877
+ if (key.backspace || key.delete) {
878
+ setScreen({ name: "name-input", value: screen.value.slice(0, -1) });
879
+ return;
880
+ }
881
+ if (input && input.length === 1 && NAME_VALID_RE.test(input)) {
882
+ if (screen.value.length >= NAME_MAX_LEN) return;
883
+ setScreen({ name: "name-input", value: screen.value + input });
884
+ }
885
+ return;
886
+ }
798
887
  if (q === "q" && screen.name !== "entropy") {
799
888
  if (screen.name === "queue" && session) {
800
889
  clearQueueCleanup();
@@ -866,32 +955,52 @@ function App({ ceremonyId: initialCeremonyId, displayName: displayName2 = "anony
866
955
  goHome();
867
956
  }
868
957
  });
958
+ if (showInfo) {
959
+ return /* @__PURE__ */ jsx7(InfoModal, {});
960
+ }
961
+ if (screen.name === "name-input") {
962
+ const { value } = screen;
963
+ const canSubmit = value.trim().length > 0;
964
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
965
+ /* @__PURE__ */ jsx7(Header, { ceremony: null }),
966
+ /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", marginTop: 1, gap: 1, children: [
967
+ /* @__PURE__ */ jsx7(Text7, { bold: true, children: "Who are you contributing as?" }),
968
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "Your display name appears next to your contributions in the public transcript. Pick anything \u2014 your real name, a handle, or hit Tab for a random anonymous name." }),
969
+ /* @__PURE__ */ jsxs7(Box7, { children: [
970
+ /* @__PURE__ */ jsx7(Text7, { color: "cyan", children: " > " }),
971
+ /* @__PURE__ */ jsx7(Text7, { children: value }),
972
+ /* @__PURE__ */ jsx7(Text7, { color: "cyan", inverse: true, children: " " })
973
+ ] }),
974
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: canSubmit ? "Enter to continue \xB7 Tab rerolls random name \xB7 \u232B backspace \xB7 Esc to quit" : "Type a name, or press Tab to suggest a random anonymous one \xB7 Esc to quit" })
975
+ ] })
976
+ ] });
977
+ }
869
978
  if (screen.name === "ceremony-picker") {
870
979
  const { ceremonies, loading } = screen;
871
- return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
872
- /* @__PURE__ */ jsx6(Header, { ceremony: null }),
873
- loading ? /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Loading ceremonies..." }) : ceremonies.length === 0 ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", gap: 1, children: [
874
- /* @__PURE__ */ jsx6(Text6, { color: "yellow", children: "No ceremonies found." }),
875
- /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "The server may not have any active ceremonies yet." }),
876
- /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Q to quit" })
877
- ] }) : /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
878
- /* @__PURE__ */ jsx6(Text6, { bold: true, children: "Select a ceremony:" }),
879
- /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: " " + "\u2500".repeat(60) }),
980
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
981
+ /* @__PURE__ */ jsx7(Header, { ceremony: null }),
982
+ loading ? /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "Loading ceremonies..." }) : ceremonies.length === 0 ? /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", gap: 1, children: [
983
+ /* @__PURE__ */ jsx7(Text7, { color: "yellow", children: "No ceremonies found." }),
984
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "The server may not have any active ceremonies yet." }),
985
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "Q to quit" })
986
+ ] }) : /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
987
+ /* @__PURE__ */ jsx7(Text7, { bold: true, children: "Select a ceremony:" }),
988
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " " + "\u2500".repeat(60) }),
880
989
  ceremonies.map((c, i) => {
881
990
  const isSelected = i === selectedIdx;
882
991
  const isOpen = c.status === "open";
883
992
  const statusColor = c.status === "open" ? "green" : c.status === "completed" ? "cyan" : "yellow";
884
- return /* @__PURE__ */ jsxs6(Box6, { gap: 2, children: [
885
- /* @__PURE__ */ jsxs6(Text6, { color: isSelected ? "cyan" : isOpen ? void 0 : "gray", children: [
993
+ return /* @__PURE__ */ jsxs7(Box7, { gap: 2, children: [
994
+ /* @__PURE__ */ jsxs7(Text7, { color: isSelected ? "cyan" : isOpen ? void 0 : "gray", children: [
886
995
  isSelected ? "\u25B6 " : " ",
887
996
  c.name.padEnd(30)
888
997
  ] }),
889
- /* @__PURE__ */ jsxs6(Text6, { color: statusColor, children: [
998
+ /* @__PURE__ */ jsxs7(Text7, { color: statusColor, children: [
890
999
  "[",
891
1000
  c.status,
892
1001
  "]"
893
1002
  ] }),
894
- /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
1003
+ /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
895
1004
  c.track_count,
896
1005
  " track",
897
1006
  c.track_count !== 1 ? "s" : "",
@@ -902,12 +1011,12 @@ function App({ ceremonyId: initialCeremonyId, displayName: displayName2 = "anony
902
1011
  ] })
903
1012
  ] }, c.id);
904
1013
  }),
905
- /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: " " + "\u2500".repeat(60) }),
1014
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " " + "\u2500".repeat(60) }),
906
1015
  (() => {
907
1016
  const c = ceremonies[selectedIdx];
908
1017
  if (!c) return null;
909
1018
  if (c.status === "initialized") {
910
- return /* @__PURE__ */ jsxs6(Text6, { color: "yellow", children: [
1019
+ return /* @__PURE__ */ jsxs7(Text7, { color: "yellow", children: [
911
1020
  " Not open yet \u2014 admin: run initialize ",
912
1021
  "<id>",
913
1022
  " then open ",
@@ -916,115 +1025,115 @@ function App({ ceremonyId: initialCeremonyId, displayName: displayName2 = "anony
916
1025
  ] });
917
1026
  }
918
1027
  if (c.status === "finalizing") {
919
- return /* @__PURE__ */ jsx6(Text6, { color: "yellow", children: " Contribution phase is closed \u2014 ceremony is applying the final beacon." });
1028
+ return /* @__PURE__ */ jsx7(Text7, { color: "yellow", children: " Contribution phase is closed \u2014 ceremony is applying the final beacon." });
920
1029
  }
921
1030
  if (c.status === "completed") {
922
- return /* @__PURE__ */ jsx6(Text6, { color: "cyan", children: " Ceremony complete \u2014 verification keys are available." });
1031
+ return /* @__PURE__ */ jsx7(Text7, { color: "cyan", children: " Ceremony complete \u2014 verification keys are available." });
923
1032
  }
924
- return /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "\u2191/\u2193 select \xB7 Enter join \xB7 Q quit" });
1033
+ return /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "\u2191/\u2193 select \xB7 Enter join \xB7 Q quit" });
925
1034
  })()
926
1035
  ] })
927
1036
  ] });
928
1037
  }
929
1038
  if (screen.name === "loading") {
930
- return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
931
- /* @__PURE__ */ jsx6(Header, { ceremony }),
932
- /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Loading tracks..." })
1039
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
1040
+ /* @__PURE__ */ jsx7(Header, { ceremony }),
1041
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "Loading tracks..." })
933
1042
  ] });
934
1043
  }
935
1044
  if (screen.name === "error") {
936
1045
  const backHint = !initialCeremonyId ? "\u232B Backspace \u2014 back to ceremony list \xB7 Q to quit" : screen.recoverable ? "\u232B Backspace / B to go back \xB7 Q to quit" : "Q to quit";
937
- return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
938
- /* @__PURE__ */ jsx6(Header, { ceremony }),
939
- /* @__PURE__ */ jsxs6(Text6, { color: "red", bold: true, children: [
1046
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
1047
+ /* @__PURE__ */ jsx7(Header, { ceremony }),
1048
+ /* @__PURE__ */ jsxs7(Text7, { color: "red", bold: true, children: [
940
1049
  "\u2717 ",
941
1050
  screen.message
942
1051
  ] }),
943
- /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: backHint })
1052
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: backHint })
944
1053
  ] });
945
1054
  }
946
1055
  if (screen.name === "tracks") {
947
1056
  const { tracks } = screen;
948
1057
  const openTracks = tracks.filter((t) => t.status === "open");
949
1058
  const myContributions = Object.values(contributed).filter((c) => c.ceremonyId === activeCeremonyId);
950
- const TabBar = () => /* @__PURE__ */ jsxs6(Box6, { gap: 1, marginBottom: 1, children: [
951
- /* @__PURE__ */ jsx6(Text6, { bold: tab === 0, color: tab === 0 ? "cyan" : void 0, dimColor: tab !== 0, children: tab === 0 ? "[ Dashboard ]" : " Dashboard " }),
952
- /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "|" }),
953
- /* @__PURE__ */ jsx6(Text6, { bold: tab === 1, color: tab === 1 ? "cyan" : void 0, dimColor: tab !== 1, children: tab === 1 ? `[ My Contributions (${myContributions.length}) ]` : ` My Contributions (${myContributions.length}) ` }),
954
- /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: " Tab to switch" })
1059
+ const TabBar = () => /* @__PURE__ */ jsxs7(Box7, { gap: 1, marginBottom: 1, children: [
1060
+ /* @__PURE__ */ jsx7(Text7, { bold: tab === 0, color: tab === 0 ? "cyan" : void 0, dimColor: tab !== 0, children: tab === 0 ? "[ Dashboard ]" : " Dashboard " }),
1061
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "|" }),
1062
+ /* @__PURE__ */ jsx7(Text7, { bold: tab === 1, color: tab === 1 ? "cyan" : void 0, dimColor: tab !== 1, children: tab === 1 ? `[ My Contributions (${myContributions.length}) ]` : ` My Contributions (${myContributions.length}) ` }),
1063
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " Tab to switch" })
955
1064
  ] });
956
- return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
957
- /* @__PURE__ */ jsx6(Header, { ceremony }),
958
- /* @__PURE__ */ jsx6(TabBar, {}),
1065
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
1066
+ /* @__PURE__ */ jsx7(Header, { ceremony }),
1067
+ /* @__PURE__ */ jsx7(TabBar, {}),
959
1068
  tab === 0 ? (
960
1069
  // ── Dashboard tab ────────────────────────────────────────────────
961
- /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
962
- /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
1070
+ /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
1071
+ /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
963
1072
  " CIRCUIT".padEnd(30),
964
1073
  "TOTAL".padEnd(10),
965
1074
  "QUEUE".padEnd(8),
966
1075
  "STATUS".padEnd(14),
967
1076
  "MY CONTRIBUTIONS"
968
1077
  ] }),
969
- /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: " " + "\u2500".repeat(68) }),
1078
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " " + "\u2500".repeat(68) }),
970
1079
  tracks.map((t, i) => {
971
1080
  const isSelected = i === selectedIdx;
972
1081
  const canContribute = t.status === "open";
973
1082
  const nameClipped = t.circuit_name.length > 26 ? t.circuit_name.slice(0, 24) + ".." : t.circuit_name;
974
1083
  const statusColor = t.status === "open" ? "green" : t.status === "finalized" ? "cyan" : "yellow";
975
1084
  const myContrib = contributed[t.id];
976
- return /* @__PURE__ */ jsxs6(Box6, { children: [
977
- /* @__PURE__ */ jsxs6(Text6, { color: isSelected ? "cyan" : canContribute ? void 0 : "gray", children: [
1085
+ return /* @__PURE__ */ jsxs7(Box7, { children: [
1086
+ /* @__PURE__ */ jsxs7(Text7, { color: isSelected ? "cyan" : canContribute ? void 0 : "gray", children: [
978
1087
  isSelected ? "\u25B6 " : " ",
979
1088
  nameClipped.padEnd(28),
980
1089
  String(t.contribution_count).padEnd(10),
981
1090
  String(t.queue_depth).padEnd(8)
982
1091
  ] }),
983
- /* @__PURE__ */ jsx6(Text6, { color: statusColor, children: t.status.padEnd(14) }),
984
- myContrib ? isSelected ? /* @__PURE__ */ jsxs6(Text6, { color: "green", children: [
1092
+ /* @__PURE__ */ jsx7(Text7, { color: statusColor, children: t.status.padEnd(14) }),
1093
+ myContrib ? isSelected ? /* @__PURE__ */ jsxs7(Text7, { color: "green", children: [
985
1094
  "\u2713 contributed (round #",
986
1095
  myContrib.sequenceNumber,
987
1096
  ") \u2014 already done"
988
- ] }) : /* @__PURE__ */ jsxs6(Text6, { color: "green", children: [
1097
+ ] }) : /* @__PURE__ */ jsxs7(Text7, { color: "green", children: [
989
1098
  "\u2713 contributed (round #",
990
1099
  myContrib.sequenceNumber,
991
1100
  ")"
992
- ] }) : canContribute ? isSelected ? /* @__PURE__ */ jsx6(Text6, { color: "yellow", children: "\u2190 Enter to contribute" }) : /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "not contributed" }) : /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "\u2014" })
1101
+ ] }) : canContribute ? isSelected ? /* @__PURE__ */ jsx7(Text7, { color: "yellow", children: "\u2190 Enter to contribute" }) : /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "not contributed" }) : /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "\u2014" })
993
1102
  ] }, t.id);
994
1103
  }),
995
- /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: " " + "\u2500".repeat(68) }),
996
- openTracks.length === 0 ? /* @__PURE__ */ jsx6(Text6, { color: "yellow", children: "No open tracks \u2014 ceremony may be finalizing or complete." }) : /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
1104
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " " + "\u2500".repeat(68) }),
1105
+ openTracks.length === 0 ? /* @__PURE__ */ jsx7(Text7, { color: "yellow", children: "No open tracks \u2014 ceremony may be finalizing or complete." }) : /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
997
1106
  "\u2191/\u2193 select \xB7 Enter contribute \xB7 R refresh \xB7 Q quit",
998
1107
  !initialCeremonyId ? " \xB7 \u232B back to ceremony list" : ""
999
1108
  ] })
1000
1109
  ] })
1001
1110
  ) : (
1002
1111
  // ── My Contributions tab ─────────────────────────────────────────
1003
- /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", children: myContributions.length === 0 ? /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", gap: 1, children: [
1004
- /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "No contributions yet." }),
1005
- /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Switch to Dashboard tab and press Enter on a circuit to contribute." })
1006
- ] }) : /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1007
- /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
1112
+ /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", children: myContributions.length === 0 ? /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", gap: 1, children: [
1113
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "No contributions yet." }),
1114
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "Switch to Dashboard tab and press Enter on a circuit to contribute." })
1115
+ ] }) : /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
1116
+ /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
1008
1117
  " CIRCUIT".padEnd(28),
1009
1118
  "ROUND".padEnd(8),
1010
1119
  "HASH".padEnd(20),
1011
1120
  "TIME"
1012
1121
  ] }),
1013
- /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: " " + "\u2500".repeat(68) }),
1014
- myContributions.map((c, i) => /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1015
- /* @__PURE__ */ jsxs6(Box6, { children: [
1016
- /* @__PURE__ */ jsx6(Text6, { color: "green", children: " \u2713 " }),
1017
- /* @__PURE__ */ jsx6(Text6, { bold: true, children: c.circuitName.padEnd(24) }),
1018
- /* @__PURE__ */ jsx6(Text6, { color: "yellow", children: "#" + c.sequenceNumber + " " }),
1019
- /* @__PURE__ */ jsx6(Text6, { color: "cyan", children: c.contributionHash ? c.contributionHash.slice(0, 16) + "..." : "(pending)" })
1122
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " " + "\u2500".repeat(68) }),
1123
+ myContributions.map((c, i) => /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
1124
+ /* @__PURE__ */ jsxs7(Box7, { children: [
1125
+ /* @__PURE__ */ jsx7(Text7, { color: "green", children: " \u2713 " }),
1126
+ /* @__PURE__ */ jsx7(Text7, { bold: true, children: c.circuitName.padEnd(24) }),
1127
+ /* @__PURE__ */ jsx7(Text7, { color: "yellow", children: "#" + c.sequenceNumber + " " }),
1128
+ /* @__PURE__ */ jsx7(Text7, { color: "cyan", children: c.contributionHash ? c.contributionHash.slice(0, 16) + "..." : "(pending)" })
1020
1129
  ] }),
1021
- /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
1130
+ /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
1022
1131
  " ",
1023
1132
  c.verifiedAt ? new Date(c.verifiedAt).toLocaleString() : ""
1024
1133
  ] })
1025
1134
  ] }, i)),
1026
- /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: " " + "\u2500".repeat(68) }),
1027
- /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
1135
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: " " + "\u2500".repeat(68) }),
1136
+ /* @__PURE__ */ jsxs7(Text7, { dimColor: true, children: [
1028
1137
  "Total: ",
1029
1138
  myContributions.length,
1030
1139
  " contribution",
@@ -1037,24 +1146,24 @@ function App({ ceremonyId: initialCeremonyId, displayName: displayName2 = "anony
1037
1146
  ] });
1038
1147
  }
1039
1148
  if (screen.name === "joining") {
1040
- return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1041
- /* @__PURE__ */ jsx6(Header, { ceremony }),
1042
- /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Joining queue..." })
1149
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
1150
+ /* @__PURE__ */ jsx7(Header, { ceremony }),
1151
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "Joining queue..." })
1043
1152
  ] });
1044
1153
  }
1045
1154
  if (screen.name === "queue") {
1046
1155
  const { trackId, circuitName } = screen;
1047
1156
  const priorContrib = contributed[trackId];
1048
- return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1049
- /* @__PURE__ */ jsx6(Header, { ceremony, subtitle: `Circuit: ${circuitName}` }),
1050
- priorContrib && /* @__PURE__ */ jsx6(Box6, { marginBottom: 1, paddingX: 1, children: /* @__PURE__ */ jsxs6(Text6, { color: "yellow", children: [
1157
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
1158
+ /* @__PURE__ */ jsx7(Header, { ceremony, subtitle: `Circuit: ${circuitName}` }),
1159
+ priorContrib && /* @__PURE__ */ jsx7(Box7, { marginBottom: 1, paddingX: 1, children: /* @__PURE__ */ jsxs7(Text7, { color: "yellow", children: [
1051
1160
  "\u26A0 You already contributed to this circuit (round #",
1052
1161
  priorContrib.sequenceNumber,
1053
1162
  ").",
1054
1163
  " ",
1055
1164
  "Contributing again is allowed and adds more entropy."
1056
1165
  ] }) }),
1057
- /* @__PURE__ */ jsx6(
1166
+ /* @__PURE__ */ jsx7(
1058
1167
  QueueView,
1059
1168
  {
1060
1169
  ceremonyId: activeCeremonyId,
@@ -1064,14 +1173,14 @@ function App({ ceremonyId: initialCeremonyId, displayName: displayName2 = "anony
1064
1173
  onError: (e) => setScreen({ name: "error", message: e.message, recoverable: true })
1065
1174
  }
1066
1175
  ),
1067
- /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "\u232B Backspace to go back \xB7 Q to quit" })
1176
+ /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "\u232B Backspace to go back \xB7 Q to quit" })
1068
1177
  ] });
1069
1178
  }
1070
1179
  if (screen.name === "entropy") {
1071
1180
  const { trackId, circuitName, slotStatus } = screen;
1072
- return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1073
- /* @__PURE__ */ jsx6(Header, { ceremony, subtitle: `Circuit: ${circuitName} \xB7 Your turn!` }),
1074
- /* @__PURE__ */ jsx6(
1181
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
1182
+ /* @__PURE__ */ jsx7(Header, { ceremony, subtitle: `Circuit: ${circuitName} \xB7 Your turn!` }),
1183
+ /* @__PURE__ */ jsx7(
1075
1184
  EntropyCollector,
1076
1185
  {
1077
1186
  onComplete: (entropy) => setScreen({ name: "contribute", trackId, circuitName, slotStatus, entropy }),
@@ -1082,9 +1191,9 @@ function App({ ceremonyId: initialCeremonyId, displayName: displayName2 = "anony
1082
1191
  }
1083
1192
  if (screen.name === "contribute") {
1084
1193
  const { trackId, circuitName, slotStatus, entropy } = screen;
1085
- return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1086
- /* @__PURE__ */ jsx6(Header, { ceremony, subtitle: `Circuit: ${circuitName} \xB7 Contributing` }),
1087
- /* @__PURE__ */ jsx6(
1194
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
1195
+ /* @__PURE__ */ jsx7(Header, { ceremony, subtitle: `Circuit: ${circuitName} \xB7 Contributing` }),
1196
+ /* @__PURE__ */ jsx7(
1088
1197
  ContributeFlow,
1089
1198
  {
1090
1199
  ceremonyId: activeCeremonyId,
@@ -1113,19 +1222,19 @@ function App({ ceremonyId: initialCeremonyId, displayName: displayName2 = "anony
1113
1222
  }
1114
1223
  if (screen.name === "done") {
1115
1224
  const { contribution } = screen;
1116
- return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1117
- /* @__PURE__ */ jsx6(Header, { ceremony }),
1118
- /* @__PURE__ */ jsx6(Attestation, { contribution }),
1119
- /* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "\u232B Backspace / B = contribute to another circuit \xB7 Q to quit" }) })
1225
+ return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
1226
+ /* @__PURE__ */ jsx7(Header, { ceremony }),
1227
+ /* @__PURE__ */ jsx7(Attestation, { contribution }),
1228
+ /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "\u232B Backspace / B = contribute to another circuit \xB7 Q to quit" }) })
1120
1229
  ] });
1121
1230
  }
1122
1231
  return null;
1123
1232
  }
1124
1233
 
1125
1234
  // src/index.tsx
1126
- import { jsx as jsx7 } from "react/jsx-runtime";
1235
+ import { jsx as jsx8 } from "react/jsx-runtime";
1127
1236
  var ceremonyId = process.env["CEREMONY_ID"] ?? "";
1128
- var displayName = process.env["CONTRIBUTOR_NAME"] ?? "anonymous";
1237
+ var displayName = process.env["CONTRIBUTOR_NAME"];
1129
1238
  process.stdout.write("\x1B[?1049h\x1B[H");
1130
1239
  function restoreScreen() {
1131
1240
  process.stdout.write("\x1B[?1049l");
@@ -1148,7 +1257,7 @@ process.on("SIGTERM", () => {
1148
1257
  });
1149
1258
  });
1150
1259
  var { waitUntilExit } = render(
1151
- /* @__PURE__ */ jsx7(App, { ceremonyId, displayName }),
1260
+ /* @__PURE__ */ jsx8(App, { ceremonyId, displayName }),
1152
1261
  { exitOnCtrlC: false }
1153
1262
  );
1154
1263
  await waitUntilExit();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umbra-privacy/ceremony",
3
- "version": "0.1.2",
3
+ "version": "0.2.3",
4
4
  "description": "Terminal UI for the Umbra Phase 2 trusted setup ceremony",
5
5
  "type": "module",
6
6
  "bin": {