pakstr 0.9.0 → 0.10.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.
@@ -23,7 +23,7 @@ const HEADER = `# pakstr.yaml — one file, one command, one APK.
23
23
  # has it can derive every app identity for every known appId. Losing it breaks
24
24
  # update continuity. It cannot rotate the signer of an already-published app.
25
25
  `;
26
- function initCommand(options = {}) {
26
+ async function initCommand(options = {}) {
27
27
  const configPath = path_1.default.resolve(options.out ?? path_1.default.join(process.cwd(), pakstrConfig_1.PAKSTR_CONFIG_FILENAME));
28
28
  if (fs_1.default.existsSync(configPath) && !options.force) {
29
29
  throw new Error(`${configPath} already exists. Use --force to overwrite, or remove it first.`);
@@ -53,8 +53,11 @@ function initCommand(options = {}) {
53
53
  }
54
54
  // Generate a fresh dev nsec into a gitignored `.env` so `pakstr run` works
55
55
  // locally with no manual setup. Never overwrites an existing PAKSTR_NSEC.
56
- const nsecStatus = ensureDevNsec();
56
+ const { nsec: devNsec, status: nsecStatus } = ensureDevNsec();
57
57
  ensureGitIgnoresEnv();
58
+ // Write zapstore.yaml (relay-side publisher whitelist) with the publisher
59
+ // npub derived from the dev nsec. Never overwrites an existing one.
60
+ const zapstoreStatus = await ensureZapstoreYaml(devNsec);
58
61
  console.log("");
59
62
  console.log("➡️ Next: run pakstr run");
60
63
  if (nsecStatus === "generated") {
@@ -63,27 +66,35 @@ function initCommand(options = {}) {
63
66
  else if (nsecStatus === "present") {
64
67
  console.log(` ${nostr_1.PAKSTR_NSEC_ENV} already in .env — left untouched.`);
65
68
  }
69
+ if (zapstoreStatus === "generated") {
70
+ console.log(" zapstore.yaml written (relay-side publisher whitelist). Commit it.");
71
+ }
72
+ else if (zapstoreStatus === "present") {
73
+ console.log(" zapstore.yaml already exists — left untouched.");
74
+ }
66
75
  console.log("");
67
76
  console.log(` For real releases, put the SAME ${nostr_1.PAKSTR_NSEC_ENV} in your CI secret manager`);
68
77
  console.log(" (reusing it is what lets an update install over the previous APK).");
69
78
  console.log(` ${nostr_1.PAKSTR_NSEC_ENV} is a release-signing root secret: anyone who has it can`);
70
79
  console.log(" sign updates for every app whose appId they know. Keep it safe.");
71
80
  }
72
- /** Generate a dev nsec into .env if none is present there. Returns what happened. */
81
+ /** Generate a dev nsec into .env if none is present there. Returns the nsec string. */
73
82
  function ensureDevNsec() {
74
83
  const envPath = path_1.default.join(process.cwd(), ".env");
75
84
  const existing = fs_1.default.existsSync(envPath)
76
85
  ? fs_1.default.readFileSync(envPath, "utf8")
77
86
  : "";
78
- if (containsPakstrNsec(existing))
79
- return "present";
87
+ if (containsPakstrNsec(existing)) {
88
+ const m = existing.match(new RegExp(`^${nostr_1.PAKSTR_NSEC_ENV}=(\\S+)`, "m"));
89
+ return { nsec: m?.[1] ?? "", status: "present" };
90
+ }
80
91
  const nsec = (0, nostr_1.encodeNsec)((0, crypto_1.randomBytes)(32));
81
92
  const line = `${nostr_1.PAKSTR_NSEC_ENV}=${nsec}\n`;
82
93
  const header = existing.length === 0 ? "# Local development secrets — never commit this file.\n" : "";
83
94
  fs_1.default.writeFileSync(envPath, header + (existing.length ? "\n" + line : line), {
84
95
  flag: existing.length === 0 ? "wx" : "a",
85
96
  });
86
- return "generated";
97
+ return { nsec, status: "generated" };
87
98
  }
88
99
  function containsPakstrNsec(envText) {
89
100
  return new RegExp(`^${nostr_1.PAKSTR_NSEC_ENV}=`, "m").test(envText);
@@ -108,6 +119,42 @@ function containsEnvEntry(gitignoreText) {
108
119
  .split(/\r?\n/)
109
120
  .some(line => line.trim() === ".env" || line.trim().startsWith(".env"));
110
121
  }
122
+ /**
123
+ * Write `zapstore.yaml` (relay-side publisher whitelist) with the publisher
124
+ * npub derived from the dev nsec. Never overwrites an existing file.
125
+ * Returns what happened.
126
+ */
127
+ async function ensureZapstoreYaml(devNsec) {
128
+ const zapstorePath = path_1.default.join(process.cwd(), "zapstore.yaml");
129
+ if (fs_1.default.existsSync(zapstorePath))
130
+ return "present";
131
+ let npub = "";
132
+ if (devNsec) {
133
+ try {
134
+ const secret = (0, nostr_1.decodeNsec)(devNsec);
135
+ const hex = await (0, nostr_1.getNpubHex)(secret);
136
+ npub = (0, nostr_1.pubkeyHexToNpub)(hex);
137
+ }
138
+ catch {
139
+ // If we can't derive the npub, write the file with a placeholder so the
140
+ // user knows to fill it in.
141
+ npub = "";
142
+ }
143
+ }
144
+ const content = `# zapstore.yaml — relay-side publisher whitelist (NIP-82 / Zap Store).
145
+ # The relay fetches this file from your repo to verify the publisher's pubkey
146
+ # is authorized for this repository. Generated by \`pakstr init\`.
147
+ #
148
+ # \`pubkey\` is the npub matching the nsec used to publish (PAKSTR_NSEC, or
149
+ # PAKSTR_PUBLISH_NSEC if you set publish.publishKey). Replace it with your
150
+ # real publisher npub for production releases if different from your dev nsec.
151
+
152
+ repository: # TODO: set to your app's source code repository URL
153
+ pubkey: ${npub || "# TODO: set to your publisher npub (npub1...)"}
154
+ `;
155
+ fs_1.default.writeFileSync(zapstorePath, content, "utf8");
156
+ return "generated";
157
+ }
111
158
  function renderConfig(c) {
112
159
  const perms = ` [${pakstrConfig_1.PERMISSION_ALIASES.map(p => `"${p}"`).join(", ")}]`;
113
160
  return `${HEADER}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pakstr",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "CLI for packaging Nostr web apps into Android APKs",
5
5
  "type": "commonjs",
6
6
  "main": "dist/index.js",