fedipod 1.49.2 → 1.50.2

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.
@@ -5,7 +5,6 @@ import fs from 'node:fs';
5
5
  import path from 'node:path';
6
6
  import { announceModeration } from '../../core/social.mjs';
7
7
  import { addRemoveActivity, publicHandle } from '../../core/wire.mjs';
8
- import { hashPassword } from '../../client/masto/index.mjs';
9
8
  import { tildify } from '../../shared/files.mjs';
10
9
  import { AGENT_VERSION } from '../static.mjs';
11
10
 
@@ -71,8 +70,6 @@ export async function get(p, ctx, req, res, url) { // eslint-disable-line no-u
71
70
  approveJoins: !!cfg.approveJoins, review: !!cfg.review,
72
71
  aliases: cfg.aliases || [],
73
72
  autoAcceptFollows: cfg.autoAcceptFollows !== false,
74
- // never the record itself
75
- hasUiPassword: !!cfg.uiPassword,
76
73
  quiescedAt: cfg.quiescedAt || null, movedTo: cfg.movedTo || null,
77
74
  // Per-machine, so it comes from the credential file, not pod config —
78
75
  // and it is a fact here, not a setting: moving it means moving data,
@@ -172,8 +169,8 @@ export async function post(p, body, ctx, req, res) { // eslint-disable-line no
172
169
  // A user acting here outranks an idle active agent on another device:
173
170
  // claim the lease rather than write state that one will clobber.
174
171
  await agent.requestTakeover?.();
175
- // Merge, never replace: the UI password and anything else set later
176
- // must survive an edit that never mentions it.
172
+ // Merge, never replace: anything set later must survive an edit that
173
+ // never mentions it.
177
174
  const cfg = { ...agent.store.getConfig() };
178
175
  if ('name' in body) {
179
176
  if (!body.name) return json(res, 400, { error: 'a display name is required' });
@@ -210,12 +207,6 @@ export async function post(p, body, ctx, req, res) { // eslint-disable-line no
210
207
  };
211
208
  cfg.moderators = next;
212
209
  }
213
- if ('password' in body) {
214
- // '' clears it. The UI password is what turns the instant OAuth
215
- // redirect into a login form; switching that off is a real choice.
216
- if (body.password) cfg.uiPassword = hashPassword(body.password);
217
- else delete cfg.uiPassword;
218
- }
219
210
  const republish = WIRE_CONFIG.some(k => k in body);
220
211
  agent.store.setConfig(cfg);
221
212
  if ('autoAcceptFollows' in body && agent.publisher) {
@@ -257,7 +248,6 @@ export async function post(p, body, ctx, req, res) { // eslint-disable-line no
257
248
  approveJoins: !!cfg.approveJoins, review: !!cfg.review,
258
249
  autoAcceptFollows: cfg.autoAcceptFollows !== false,
259
250
  ...(isGroup() ? { moderators: cfg.moderators || [] } : {}),
260
- hasUiPassword: !!cfg.uiPassword,
261
251
  },
262
252
  });
263
253
  }
@@ -239,8 +239,7 @@ export function buildAdminSurface({ agent, gate, allowed, log = console.log,
239
239
  // twice over: a group has statuses (what it carried) and notifications
240
240
  // (who joined), and its operator has a bio to edit and a profile they
241
241
  // want to see the way everyone else does. The surface this opens is a
242
- // login and client tokens, and `passwd` has never had a group carve-out,
243
- // so a group can be gated exactly like a person before it is exposed.
242
+ // login and client tokens, approved at the pod like a person's.
244
243
  if (p.startsWith('/api/') || p.startsWith('/oauth/')) {
245
244
  if (await masto.handle(req, res, p, url)) return;
246
245
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fedipod",
3
- "version": "1.49.2",
3
+ "version": "1.50.2",
4
4
  "description": "Standalone single-actor ActivityPub agent whose wire face, RDF truth and state all live on a Solid pod (CSS). Bundles a Phanpy UI and a Mastodon client-API facade.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -55,7 +55,7 @@
55
55
  "install-service": "node bin/fedipod.mjs install-service",
56
56
  "uninstall-service": "node bin/fedipod.mjs uninstall-service",
57
57
  "prepublishOnly": "npm test",
58
- "test": "node claude/smoke-tests/agent-smoke.mjs && node claude/smoke-tests/front-hard-test.mjs && node claude/smoke-tests/copy-test.mjs && node claude/smoke-tests/copy-mode-test.mjs && node claude/smoke-tests/hold-test.mjs && node claude/smoke-tests/masto-gateway-test.mjs && node scripts/check-pod-calls.mjs && node scripts/verify-ui.mjs && node --test claude/smoke-tests/forum-signup-form.test.mjs claude/smoke-tests/door-verify.test.mjs claude/smoke-tests/oauth-signin.test.mjs claude/smoke-tests/app-registration.test.mjs claude/smoke-tests/push-signout.test.mjs claude/smoke-tests/token-fetches.test.mjs claude/smoke-tests/signin-hardening.test.mjs claude/smoke-tests/load-fixes.test.mjs && node scripts/check-sizes.mjs && node scripts/test-dependents.mjs && node claude/validation/browser-agent/all.mjs",
58
+ "test": "node claude/smoke-tests/agent-smoke.mjs && node claude/smoke-tests/front-hard-test.mjs && node claude/smoke-tests/copy-test.mjs && node claude/smoke-tests/copy-mode-test.mjs && node claude/smoke-tests/hold-test.mjs && node claude/smoke-tests/masto-gateway-test.mjs && node scripts/check-pod-calls.mjs && node scripts/verify-ui.mjs && node --test claude/smoke-tests/forum-signup-form.test.mjs claude/smoke-tests/door-verify.test.mjs claude/smoke-tests/oauth-signin.test.mjs claude/smoke-tests/app-registration.test.mjs claude/smoke-tests/push-signout.test.mjs claude/smoke-tests/token-fetches.test.mjs claude/smoke-tests/signin-hardening.test.mjs claude/smoke-tests/load-fixes.test.mjs claude/smoke-tests/unread-docs.test.mjs claude/smoke-tests/post-text-once.test.mjs && node scripts/check-sizes.mjs && node scripts/test-dependents.mjs && node claude/validation/browser-agent/all.mjs",
59
59
  "verify-ui": "node scripts/verify-ui.mjs",
60
60
  "build:sw": "node scripts/build-app.mjs --entry web/app/sw-src.mjs --out web/app/dist/sw.js",
61
61
  "build:boot": "node scripts/build-app.mjs --entry web/app/boot.mjs --out web/app/dist/boot.js",
@@ -220,7 +220,7 @@ pre { overflow-x: auto; background: #0001; padding: .6rem; border-radius: .3rem;
220
220
  no page there to open. Plain links — a top-level navigation sends no
221
221
  Origin, so each agent's own firewall lets it through. -->
222
222
  <section id="pane-others" aria-label="Actors on this machine" hidden>
223
- <!-- Everything setup cannot work without. The bio, avatar and UI password it
223
+ <!-- Everything setup cannot work without. The bio and avatar it
224
224
  also offers are all changeable afterwards, so they are not asked here —
225
225
  the fuller form is what the new actor opens on. -->
226
226
  <form id="new-actor-form" hidden>
@@ -7,9 +7,6 @@
7
7
  // Two rules the routes enforce and this page respects: a write is a MERGE, so a
8
8
  // form that never mentions a field cannot delete it; and anything on the wire is
9
9
  // not real until the actor is republished, which POST /config does for itself.
10
- //
11
- // The UI password is deliberately not here — `fedipod passwd` sets it. It
12
- // only gates /oauth/authorize, so it does nothing for a loopback-only agent.
13
10
 
14
11
  let config = null;
15
12
  // Held, because render() moves it into a generated row and empties that row's
@@ -18,7 +18,6 @@ import { publicHandle, webfingerHost } from '../../lib/core/wire.mjs';
18
18
  import * as podInbox from '../../lib/pod/inbox.mjs';
19
19
  import { normalizeImport, IMPORT_KINDS } from '../../lib/connections/import.mjs';
20
20
  import { followActor, followHandle } from '../../lib/core/social.mjs';
21
- import { hashPassword } from '../../lib/client/masto/index.mjs';
22
21
 
23
22
  // The identity itself — changing any means a different actor, i.e. a new setup.
24
23
  const PERMANENT_CONFIG = ['handle', 'remotePod', 'issuer', 'root', 'kind'];
@@ -121,7 +120,6 @@ export class AdminFacade {
121
120
  image: cfg.image || null, fields: cfg.fields || [],
122
121
  aliases: cfg.aliases || [],
123
122
  autoAcceptFollows: cfg.autoAcceptFollows !== false,
124
- hasUiPassword: !!cfg.uiPassword,
125
123
  quiescedAt: cfg.quiescedAt || null, movedTo: cfg.movedTo || null,
126
124
  mode: a.status().mode,
127
125
  // Live from the credential's actual backend on THIS browser (a
@@ -218,10 +216,6 @@ export class AdminFacade {
218
216
  .map(f => ({ name: String(f.name).trim(), value: String(f.value ?? '').trim() }));
219
217
  }
220
218
  if ('autoAcceptFollows' in body) cfg.autoAcceptFollows = !!body.autoAcceptFollows;
221
- if ('password' in body) {
222
- if (body.password) cfg.uiPassword = hashPassword(body.password);
223
- else delete cfg.uiPassword;
224
- }
225
219
  const republish = WIRE_CONFIG.some(k => k in body);
226
220
  a.store.setConfig(cfg);
227
221
  if ('autoAcceptFollows' in body && a.publisher) a.publisher.config.autoAcceptFollows = cfg.autoAcceptFollows;
package/web/app/agent.mjs CHANGED
@@ -516,7 +516,9 @@ export class BrowserAgent {
516
516
  let unread = null;
517
517
  const fullLoad = () => this.store.load().catch((e) => { unread = e; });
518
518
  // Warm: only documents caught mid-write are read again, else a full load.
519
- if (this._warm) await this.store.refresh(this._warm.pending).catch(fullLoad);
519
+ // A kept copy without the container's ETag is one whose last load could
520
+ // not read everything: what it lacks is still on the pod, unread.
521
+ if (this._warm && warmFrom.listingEtag) await this.store.refresh(this._warm.pending).catch(fullLoad);
520
522
  else await fullLoad();
521
523
  // Config: handed in on sign-up, or read from the pod on a returning sign-in.
522
524
  const cfg = config || this.store.getConfig();