Remove AdminSigner, Conf.pubkey, Conf.nsec, add Conf.signer

This commit is contained in:
Alex Gleason 2025-02-20 12:04:52 -06:00
parent 5ad38b4058
commit 0841563d69
No known key found for this signature in database
GPG key ID: 7211D1F99744FBB7
36 changed files with 135 additions and 125 deletions

View file

@ -10,7 +10,7 @@ Deno.test('confMw', async () => {
const app = new Hono();
app.get('/', confMw(env), (c) => c.text(c.var.conf.pubkey));
app.get('/', confMw(env), async (c) => c.text(await c.var.conf.signer.getPublicKey()));
const response = await app.request('/');
const body = await response.text();

View file

@ -9,12 +9,11 @@ Deno.test('DittoConfig', async (t) => {
const config = new DittoConf(env);
await t.step('nsec', () => {
assertEquals(config.nsec, 'nsec19shyxpuzd0cq2p5078fwnws7tyykypud6z205fzhlmlrs2vpz6hs83zwkw');
});
await t.step('pubkey', () => {
assertEquals(config.pubkey, '1ba0c5ed1bbbf3b7eb0d7843ba16836a0201ea68a76bafcba507358c45911ff6');
await t.step('signer', async () => {
assertEquals(
await config.signer.getPublicKey(),
'1ba0c5ed1bbbf3b7eb0d7843ba16836a0201ea68a76bafcba507358c45911ff6',
);
});
});
@ -22,8 +21,8 @@ Deno.test('DittoConfig defaults', async (t) => {
const env = new Map<string, string>();
const config = new DittoConf(env);
await t.step('nsec throws', () => {
assertThrows(() => config.nsec);
await t.step('signer throws', () => {
assertThrows(() => config.signer);
});
await t.step('port', () => {

View file

@ -1,10 +1,11 @@
import os from 'node:os';
import path from 'node:path';
import ISO6391, { type LanguageCode } from 'iso-639-1';
import { getPublicKey, nip19 } from 'nostr-tools';
import { NSecSigner } from '@nostrify/nostrify';
import { decodeBase64 } from '@std/encoding/base64';
import { encodeBase64Url } from '@std/encoding/base64url';
import ISO6391, { type LanguageCode } from 'iso-639-1';
import { nip19 } from 'nostr-tools';
import { getEcdsaPublicKey } from './utils/crypto.ts';
import { optionalBooleanSchema, optionalNumberSchema } from './utils/schema.ts';
@ -14,35 +15,36 @@ import { mergeURLPath } from './utils/url.ts';
export class DittoConf {
constructor(private env: { get(key: string): string | undefined }) {}
/** Cached parsed admin pubkey value. */
private _pubkey: string | undefined;
/** Cached parsed admin signer. */
private _signer: NSecSigner | undefined;
/** Cached parsed VAPID public key value. */
private _vapidPublicKey: Promise<string | undefined> | undefined;
/** Ditto admin secret key in nip19 format. This is the way it's configured by an admin. */
get nsec(): `nsec1${string}` {
const value = this.env.get('DITTO_NSEC');
if (!value) {
/**
* Ditto admin secret key in hex format.
* @deprecated Use `signer` instead. TODO: handle auth tokens.
*/
get seckey(): Uint8Array {
const nsec = this.env.get('DITTO_NSEC');
if (!nsec) {
throw new Error('Missing DITTO_NSEC');
}
if (!value.startsWith('nsec1')) {
if (!nsec.startsWith('nsec1')) {
throw new Error('Invalid DITTO_NSEC');
}
return value as `nsec1${string}`;
return nip19.decode(nsec as `nsec1${string}`).data;
}
/** Ditto admin secret key in hex format. */
get seckey(): Uint8Array {
return nip19.decode(this.nsec).data;
/** Ditto admin signer. */
get signer(): NSecSigner {
if (!this._signer) {
this._signer = new NSecSigner(this.seckey);
}
/** Ditto admin public key in hex format. */
get pubkey(): string {
if (!this._pubkey) {
this._pubkey = getPublicKey(this.seckey);
}
return this._pubkey;
return this._signer;
}
/** Port to use when serving the HTTP server. */

View file

@ -211,7 +211,9 @@ const accountStatusesController: AppController = async (c) => {
const [[author], [user]] = await Promise.all([
store.query([{ kinds: [0], authors: [pubkey], limit: 1 }], { signal }),
store.query([{ kinds: [30382], authors: [conf.pubkey], '#d': [pubkey], limit: 1 }], { signal }),
store.query([{ kinds: [30382], authors: [await conf.signer.getPublicKey()], '#d': [pubkey], limit: 1 }], {
signal,
}),
]);
if (author) {

View file

@ -43,13 +43,15 @@ const adminAccountsController: AppController = async (c) => {
staff,
} = adminAccountQuerySchema.parse(c.req.query());
const adminPubkey = await conf.signer.getPublicKey();
if (pending) {
if (disabled || silenced || suspended || sensitized) {
return c.json([]);
}
const orig = await store.query(
[{ kinds: [30383], authors: [conf.pubkey], '#k': ['3036'], '#n': ['pending'], ...params }],
[{ kinds: [30383], authors: [adminPubkey], '#k': ['3036'], '#n': ['pending'], ...params }],
{ signal },
);
@ -86,7 +88,10 @@ const adminAccountsController: AppController = async (c) => {
n.push('moderator');
}
const events = await store.query([{ kinds: [30382], authors: [conf.pubkey], '#n': n, ...params }], { signal });
const events = await store.query(
[{ kinds: [30382], authors: [adminPubkey], '#n': n, ...params }],
{ signal },
);
const pubkeys = new Set<string>(
events
@ -157,9 +162,11 @@ const adminActionController: AppController = async (c) => {
}
if (data.type === 'revoke_name') {
n.revoke_name = true;
store.remove([{ kinds: [30360], authors: [conf.pubkey], '#p': [authorId] }]).catch((e: unknown) => {
store.remove([{ kinds: [30360], authors: [await conf.signer.getPublicKey()], '#p': [authorId] }]).catch(
(e: unknown) => {
logi({ level: 'error', ns: 'ditto.api.admin.account.action', type: data.type, error: errorJson(e) });
});
},
);
}
await updateUser(authorId, n, c);
@ -185,7 +192,10 @@ const adminApproveController: AppController = async (c) => {
return c.json({ error: 'Invalid NIP-05' }, 400);
}
const [existing] = await store.query([{ kinds: [30360], authors: [conf.pubkey], '#d': [r], limit: 1 }]);
const [existing] = await store.query([
{ kinds: [30360], authors: [await conf.signer.getPublicKey()], '#d': [r], limit: 1 },
]);
if (existing) {
return c.json({ error: 'NIP-05 already granted to another user' }, 400);
}

View file

@ -9,7 +9,6 @@ import { createEvent, paginated, parseBody, updateAdminEvent } from '@/utils/api
import { getInstanceMetadata } from '@/utils/instance.ts';
import { deleteTag } from '@/utils/tags.ts';
import { DittoZapSplits, getZapSplits } from '@/utils/zap-split.ts';
import { AdminSigner } from '@/signers/AdminSigner.ts';
import { screenshotsSchema } from '@/schemas/nostr.ts';
import { booleanParamSchema, percentageSchema, wsUrlSchema } from '@/schema.ts';
import { hydrateEvents } from '@/storages/hydrate.ts';
@ -33,7 +32,7 @@ export const adminRelaysController: AppController = async (c) => {
const store = await Storages.db();
const [event] = await store.query([
{ kinds: [10002], authors: [conf.pubkey], limit: 1 },
{ kinds: [10002], authors: [await conf.signer.getPublicKey()], limit: 1 },
]);
if (!event) {
@ -44,10 +43,11 @@ export const adminRelaysController: AppController = async (c) => {
};
export const adminSetRelaysController: AppController = async (c) => {
const { conf } = c.var;
const store = await Storages.db();
const relays = relaySchema.array().parse(await c.req.json());
const event = await new AdminSigner().signEvent({
const event = await conf.signer.signEvent({
kind: 10002,
tags: relays.map(({ url, marker }) => marker ? ['r', url, marker] : ['r', url]),
content: '',
@ -98,7 +98,7 @@ export const nameRequestController: AppController = async (c) => {
['r', name],
['L', 'nip05.domain'],
['l', name.split('@')[1], 'nip05.domain'],
['p', conf.pubkey],
['p', await conf.signer.getPublicKey()],
],
}, c);
@ -124,7 +124,7 @@ export const nameRequestsController: AppController = async (c) => {
const filter: NostrFilter = {
kinds: [30383],
authors: [conf.pubkey],
authors: [await conf.signer.getPublicKey()],
'#k': ['3036'],
'#p': [pubkey],
...params,
@ -179,7 +179,9 @@ export const updateZapSplitsController: AppController = async (c) => {
return c.json({ error: result.error }, 400);
}
const dittoZapSplit = await getZapSplits(store, conf.pubkey);
const adminPubkey = await conf.signer.getPublicKey();
const dittoZapSplit = await getZapSplits(store, adminPubkey);
if (!dittoZapSplit) {
return c.json({ error: 'Zap split not activated, restart the server.' }, 404);
}
@ -192,7 +194,7 @@ export const updateZapSplitsController: AppController = async (c) => {
}
await updateListAdminEvent(
{ kinds: [30078], authors: [conf.pubkey], '#d': ['pub.ditto.zapSplits'], limit: 1 },
{ kinds: [30078], authors: [adminPubkey], '#d': ['pub.ditto.zapSplits'], limit: 1 },
(tags) =>
pubkeys.reduce((accumulator, pubkey) => {
return addTag(accumulator, ['p', pubkey, data[pubkey].weight.toString(), data[pubkey].message]);
@ -215,7 +217,9 @@ export const deleteZapSplitsController: AppController = async (c) => {
return c.json({ error: result.error }, 400);
}
const dittoZapSplit = await getZapSplits(store, conf.pubkey);
const adminPubkey = await conf.signer.getPublicKey();
const dittoZapSplit = await getZapSplits(store, adminPubkey);
if (!dittoZapSplit) {
return c.json({ error: 'Zap split not activated, restart the server.' }, 404);
}
@ -223,7 +227,7 @@ export const deleteZapSplitsController: AppController = async (c) => {
const { data } = result;
await updateListAdminEvent(
{ kinds: [30078], authors: [conf.pubkey], '#d': ['pub.ditto.zapSplits'], limit: 1 },
{ kinds: [30078], authors: [adminPubkey], '#d': ['pub.ditto.zapSplits'], limit: 1 },
(tags) =>
data.reduce((accumulator, currentValue) => {
return deleteTag(accumulator, ['p', currentValue]);
@ -238,7 +242,7 @@ export const getZapSplitsController: AppController = async (c) => {
const { conf } = c.var;
const store = c.get('store');
const dittoZapSplit: DittoZapSplits | undefined = await getZapSplits(store, conf.pubkey) ?? {};
const dittoZapSplit: DittoZapSplits | undefined = await getZapSplits(store, await conf.signer.getPublicKey()) ?? {};
if (!dittoZapSplit) {
return c.json({ error: 'Zap split not activated, restart the server.' }, 404);
}
@ -311,7 +315,7 @@ export const updateInstanceController: AppController = async (c) => {
const { conf } = c.var;
const body = await parseBody(c.req.raw);
const result = updateInstanceSchema.safeParse(body);
const pubkey = conf.pubkey;
const pubkey = await conf.signer.getPublicKey();
if (!result.success) {
return c.json(result.error, 422);

View file

@ -68,7 +68,7 @@ const instanceV1Controller: AppController = async (c) => {
version,
email: meta.email,
nostr: {
pubkey: conf.pubkey,
pubkey: await conf.signer.getPublicKey(),
relay: `${wsProtocol}//${host}/relay`,
},
rules: [],
@ -141,7 +141,7 @@ const instanceV2Controller: AppController = async (c) => {
},
},
nostr: {
pubkey: conf.pubkey,
pubkey: await conf.signer.getPublicKey(),
relay: `${wsProtocol}//${host}/relay`,
},
pleroma: {

View file

@ -68,7 +68,7 @@ const notificationsController: AppController = async (c) => {
}
if (types.has('ditto:name_grant') && !account_id) {
filters.push({ kinds: [30360], authors: [conf.pubkey], '#p': [pubkey], ...params });
filters.push({ kinds: [30360], authors: [await conf.signer.getPublicKey()], '#p': [pubkey], ...params });
}
return renderNotifications(filters, types, params, c);

View file

@ -2,7 +2,6 @@ import { z } from 'zod';
import { type AppController } from '@/app.ts';
import { configSchema, elixirTupleSchema } from '@/schemas/pleroma-api.ts';
import { AdminSigner } from '@/signers/AdminSigner.ts';
import { Storages } from '@/storages.ts';
import { createAdminEvent, updateAdminEvent, updateUser } from '@/utils/api.ts';
import { lookupPubkey } from '@/utils/lookup.ts';
@ -34,7 +33,6 @@ const configController: AppController = async (c) => {
/** Pleroma admin config controller. */
const updateConfigController: AppController = async (c) => {
const { conf } = c.var;
const { pubkey } = conf;
const store = await Storages.db();
const configs = await getPleromaConfigs(store, c.req.raw.signal);
@ -44,7 +42,7 @@ const updateConfigController: AppController = async (c) => {
await createAdminEvent({
kind: 30078,
content: await new AdminSigner().nip44.encrypt(pubkey, JSON.stringify(configs)),
content: await conf.signer.nip44.encrypt(await conf.signer.getPublicKey(), JSON.stringify(configs)),
tags: [
['d', 'pub.ditto.pleroma.config'],
['encrypted', 'nip44'],
@ -77,7 +75,7 @@ const pleromaAdminTagController: AppController = async (c) => {
if (!pubkey) continue;
await updateAdminEvent(
{ kinds: [30382], authors: [conf.pubkey], '#d': [pubkey], limit: 1 },
{ kinds: [30382], authors: [await conf.signer.getPublicKey()], '#d': [pubkey], limit: 1 },
(prev) => {
const tags = prev?.tags ?? [['d', pubkey]];
@ -110,7 +108,7 @@ const pleromaAdminUntagController: AppController = async (c) => {
if (!pubkey) continue;
await updateAdminEvent(
{ kinds: [30382], authors: [conf.pubkey], '#d': [pubkey], limit: 1 },
{ kinds: [30382], authors: [await conf.signer.getPublicKey()], '#d': [pubkey], limit: 1 },
(prev) => ({
kind: 30382,
content: prev?.content ?? '',

View file

@ -36,7 +36,7 @@ const reportController: AppController = async (c) => {
const tags = [
['p', account_id, category],
['P', conf.pubkey],
['P', await conf.signer.getPublicKey()],
];
for (const status of status_ids) {
@ -70,7 +70,7 @@ const adminReportsController: AppController = async (c) => {
const filter: NostrFilter = {
kinds: [30383],
authors: [conf.pubkey],
authors: [await conf.signer.getPublicKey()],
'#k': ['1984'],
...params,
};

View file

@ -196,7 +196,7 @@ const createStatusController: AppController = async (c) => {
if (conf.zapSplitsEnabled) {
const meta = n.json().pipe(n.metadata()).catch({}).parse(author?.content);
const lnurl = getLnurl(meta);
const dittoZapSplit = await getZapSplits(store, conf.pubkey);
const dittoZapSplit = await getZapSplits(store, await conf.signer.getPublicKey());
if (lnurl && dittoZapSplit) {
const totalSplit = Object.values(dittoZapSplit).reduce((total, { weight }) => total + weight, 0);
for (const zapPubkey in dittoZapSplit) {

View file

@ -31,8 +31,8 @@ async function renderV2Suggestions(c: AppContext, params: { offset: number; limi
const pubkey = await signer?.getPublicKey();
const filters: NostrFilter[] = [
{ kinds: [30382], authors: [conf.pubkey], '#n': ['suggested'], limit },
{ kinds: [1985], '#L': ['pub.ditto.trends'], '#l': [`#p`], authors: [conf.pubkey], limit: 1 },
{ kinds: [30382], authors: [await conf.signer.getPublicKey()], '#n': ['suggested'], limit },
{ kinds: [1985], '#L': ['pub.ditto.trends'], '#l': [`#p`], authors: [await conf.signer.getPublicKey()], limit: 1 },
];
if (pubkey) {
@ -41,13 +41,20 @@ async function renderV2Suggestions(c: AppContext, params: { offset: number; limi
}
const events = await store.query(filters, { signal });
const adminPubkey = await conf.signer.getPublicKey();
const [userEvents, followsEvent, mutesEvent, trendingEvent] = [
events.filter((event) => matchFilter({ kinds: [30382], authors: [conf.pubkey], '#n': ['suggested'] }, event)),
events.filter((event) => matchFilter({ kinds: [30382], authors: [adminPubkey], '#n': ['suggested'] }, event)),
pubkey ? events.find((event) => matchFilter({ kinds: [3], authors: [pubkey] }, event)) : undefined,
pubkey ? events.find((event) => matchFilter({ kinds: [10000], authors: [pubkey] }, event)) : undefined,
events.find((event) =>
matchFilter({ kinds: [1985], '#L': ['pub.ditto.trends'], '#l': [`#p`], authors: [conf.pubkey], limit: 1 }, event)
matchFilter({
kinds: [1985],
'#L': ['pub.ditto.trends'],
'#l': [`#p`],
authors: [adminPubkey],
limit: 1,
}, event)
),
];
@ -95,7 +102,7 @@ export const localSuggestionsController: AppController = async (c) => {
const store = c.get('store');
const grants = await store.query(
[{ kinds: [30360], authors: [conf.pubkey], ...params }],
[{ kinds: [30360], authors: [await conf.signer.getPublicKey()], ...params }],
{ signal },
);

View file

@ -95,7 +95,7 @@ const suggestedTimelineController: AppController = async (c) => {
const params = c.get('pagination');
const [follows] = await store.query(
[{ kinds: [3], authors: [conf.pubkey], limit: 1 }],
[{ kinds: [3], authors: [await conf.signer.getPublicKey()], limit: 1 }],
);
const authors = [...getTagSet(follows?.tags ?? [], 'p')];

View file

@ -53,7 +53,7 @@ const trendingTagsController: AppController = async (c) => {
async function getTrendingHashtags(conf: DittoConf) {
const store = await Storages.db();
const trends = await getTrendingTags(store, 't', conf.pubkey);
const trends = await getTrendingTags(store, 't', await conf.signer.getPublicKey());
return trends.map((trend) => {
const hashtag = trend.value;
@ -106,7 +106,7 @@ const trendingLinksController: AppController = async (c) => {
async function getTrendingLinks(conf: DittoConf) {
const store = await Storages.db();
const trends = await getTrendingTags(store, 'r', conf.pubkey);
const trends = await getTrendingTags(store, 'r', await conf.signer.getPublicKey());
return Promise.all(trends.map(async (trend) => {
const link = trend.value;
@ -148,7 +148,7 @@ const trendingStatusesController: AppController = async (c) => {
kinds: [1985],
'#L': ['pub.ditto.trends'],
'#l': ['#e'],
authors: [conf.pubkey],
authors: [await conf.signer.getPublicKey()],
until,
limit: 1,
}]);

View file

@ -14,7 +14,7 @@ const relayInfoController: AppController = async (c) => {
return c.json({
name: meta.name,
description: meta.about,
pubkey: conf.pubkey,
pubkey: await conf.signer.getPublicKey(),
contact: meta.email,
supported_nips: [1, 5, 9, 11, 16, 45, 50, 46, 98],
software: 'Ditto',

View file

@ -40,7 +40,7 @@ function requireRole(role: UserRole, opts?: ParseAuthRequestOpts): AppMiddleware
const [user] = await store.query([{
kinds: [30382],
authors: [conf.pubkey],
authors: [await conf.signer.getPublicKey()],
'#d': [proof.pubkey],
limit: 1,
}]);

View file

@ -11,7 +11,6 @@ import { Conf } from '@/config.ts';
import { DittoPush } from '@/DittoPush.ts';
import { DittoEvent } from '@/interfaces/DittoEvent.ts';
import { RelayError } from '@/RelayError.ts';
import { AdminSigner } from '@/signers/AdminSigner.ts';
import { hydrateEvents } from '@/storages/hydrate.ts';
import { Storages } from '@/storages.ts';
import { eventAge, Time } from '@/utils.ts';
@ -83,7 +82,7 @@ async function handleEvent(event: DittoEvent, opts: PipelineOpts): Promise<void>
}
// Ensure the event doesn't violate the policy.
if (event.pubkey !== Conf.pubkey) {
if (event.pubkey !== await Conf.signer.getPublicKey()) {
await policyFilter(event, opts.signal);
}
@ -297,11 +296,12 @@ async function webPush(event: NostrEvent): Promise<void> {
}
async function generateSetEvents(event: NostrEvent): Promise<void> {
const tagsAdmin = event.tags.some(([name, value]) => ['p', 'P'].includes(name) && value === Conf.pubkey);
const signer = Conf.signer;
const pubkey = await signer.getPublicKey();
const tagsAdmin = event.tags.some(([name, value]) => ['p', 'P'].includes(name) && value === pubkey);
if (event.kind === 1984 && tagsAdmin) {
const signer = new AdminSigner();
const rel = await signer.signEvent({
kind: 30383,
content: '',
@ -310,8 +310,8 @@ async function generateSetEvents(event: NostrEvent): Promise<void> {
['p', event.pubkey],
['k', '1984'],
['n', 'open'],
...[...getTagSet(event.tags, 'p')].map((pubkey) => ['P', pubkey]),
...[...getTagSet(event.tags, 'e')].map((pubkey) => ['e', pubkey]),
...[...getTagSet(event.tags, 'p')].map((value) => ['P', value]),
...[...getTagSet(event.tags, 'e')].map((value) => ['e', value]),
],
created_at: Math.floor(Date.now() / 1000),
});
@ -320,8 +320,6 @@ async function generateSetEvents(event: NostrEvent): Promise<void> {
}
if (event.kind === 3036 && tagsAdmin) {
const signer = new AdminSigner();
const rel = await signer.signEvent({
kind: 30383,
content: '',

View file

@ -1,9 +0,0 @@
import { NSecSigner } from '@nostrify/nostrify';
import { Conf } from '@/config.ts';
/** Sign events as the Ditto server. */
export class AdminSigner extends NSecSigner {
constructor() {
super(Conf.seckey);
}
}

View file

@ -42,7 +42,7 @@ export class Storages {
const db = await this.database();
const store = new DittoPgStore({
db,
pubkey: Conf.pubkey,
pubkey: await Conf.signer.getPublicKey(),
timeout: Conf.db.timeouts.default,
notify: Conf.notifyEnabled,
});
@ -68,7 +68,7 @@ export class Storages {
const db = await this.db();
const [relayList] = await db.query([
{ kinds: [10002], authors: [Conf.pubkey], limit: 1 },
{ kinds: [10002], authors: [await Conf.signer.getPublicKey()], limit: 1 },
]);
const tags = relayList?.tags ?? [];

View file

@ -18,15 +18,17 @@ export class AdminStore implements NStore {
const users = await this.store.query([{
kinds: [30382],
authors: [Conf.pubkey],
authors: [await Conf.signer.getPublicKey()],
'#d': [...pubkeys],
limit: pubkeys.size,
}]);
const adminPubkey = await Conf.signer.getPublicKey();
return events.filter((event) => {
const user = users.find(
({ kind, pubkey, tags }) =>
kind === 30382 && pubkey === Conf.pubkey && tags.find(([name]) => name === 'd')?.[1] === event.pubkey,
kind === 30382 && pubkey === adminPubkey && tags.find(([name]) => name === 'd')?.[1] === event.pubkey,
);
const n = getTagSet(user?.tags ?? [], 'n');

View file

@ -10,5 +10,5 @@ const testStats = JSON.parse(await Deno.readTextFile('fixtures/stats.json'));
const events = testEvents.slice(0, 20);
Deno.bench('assembleEvents with home feed', () => {
assembleEvents(events, testEvents, testStats);
assembleEvents('', events, testEvents, testStats);
});

View file

@ -79,15 +79,18 @@ async function hydrateEvents(opts: HydrateOpts): Promise<DittoEvent[]> {
// Dedupe events.
const results = [...new Map(cache.map((event) => [event.id, event])).values()];
const admin = await Conf.signer.getPublicKey();
// First connect all the events to each-other, then connect the connected events to the original list.
assembleEvents(results, results, stats);
assembleEvents(events, results, stats);
assembleEvents(admin, results, results, stats);
assembleEvents(admin, events, results, stats);
return events;
}
/** Connect the events in list `b` to the DittoEvent fields in list `a`. */
export function assembleEvents(
admin: string,
a: DittoEvent[],
b: DittoEvent[],
stats: {
@ -96,8 +99,6 @@ export function assembleEvents(
favicons: Record<string, string>;
},
): DittoEvent[] {
const admin = Conf.pubkey;
const authorStats = stats.authors.reduce((result, { pubkey, ...stat }) => {
result[pubkey] = {
...stat,
@ -316,7 +317,7 @@ async function gatherProfiles({ events, store, signal }: HydrateOpts): Promise<D
}
/** Collect users from the events. */
function gatherUsers({ events, store, signal }: HydrateOpts): Promise<DittoEvent[]> {
async function gatherUsers({ events, store, signal }: HydrateOpts): Promise<DittoEvent[]> {
const pubkeys = new Set(events.map((event) => event.pubkey));
if (!pubkeys.size) {
@ -324,13 +325,13 @@ function gatherUsers({ events, store, signal }: HydrateOpts): Promise<DittoEvent
}
return store.query(
[{ kinds: [30382], authors: [Conf.pubkey], '#d': [...pubkeys], limit: pubkeys.size }],
[{ kinds: [30382], authors: [await Conf.signer.getPublicKey()], '#d': [...pubkeys], limit: pubkeys.size }],
{ signal },
);
}
/** Collect info events from the events. */
function gatherInfo({ events, store, signal }: HydrateOpts): Promise<DittoEvent[]> {
async function gatherInfo({ events, store, signal }: HydrateOpts): Promise<DittoEvent[]> {
const ids = new Set<string>();
for (const event of events) {
@ -344,7 +345,7 @@ function gatherInfo({ events, store, signal }: HydrateOpts): Promise<DittoEvent[
}
return store.query(
[{ kinds: [30383], authors: [Conf.pubkey], '#d': [...ids], limit: ids.size }],
[{ kinds: [30383], authors: [await Conf.signer.getPublicKey()], '#d': [...ids], limit: ids.size }],
{ signal },
);
}

View file

@ -20,7 +20,7 @@ export async function createTestDB(opts?: { pure?: boolean }) {
const store = new DittoPgStore({
db,
timeout: Conf.db.timeouts.default,
pubkey: Conf.pubkey,
pubkey: await Conf.signer.getPublicKey(),
pure: opts?.pure ?? false,
notify: true,
});

View file

@ -5,7 +5,6 @@ import { Kysely, sql } from 'kysely';
import { Conf } from '@/config.ts';
import { handleEvent } from '@/pipeline.ts';
import { AdminSigner } from '@/signers/AdminSigner.ts';
import { Storages } from '@/storages.ts';
import { errorJson } from '@/utils/log.ts';
import { Time } from '@/utils/time.ts';
@ -100,7 +99,7 @@ export async function updateTrendingTags(
return;
}
const signer = new AdminSigner();
const signer = Conf.signer;
const label = await signer.signEvent({
kind: 1985,

View file

@ -9,7 +9,6 @@ import { type AppContext } from '@/app.ts';
import { Conf } from '@/config.ts';
import * as pipeline from '@/pipeline.ts';
import { RelayError } from '@/RelayError.ts';
import { AdminSigner } from '@/signers/AdminSigner.ts';
import { Storages } from '@/storages.ts';
import { nostrNow } from '@/utils.ts';
import { parseFormData } from '@/utils/formdata.ts';
@ -81,7 +80,7 @@ function updateListEvent(
/** Publish an admin event through the pipeline. */
async function createAdminEvent(t: EventStub, c: AppContext): Promise<NostrEvent> {
const signer = new AdminSigner();
const signer = Conf.signer;
const event = await signer.signEvent({
content: '',
@ -126,7 +125,7 @@ function updateEventInfo(id: string, n: Record<string, boolean>, c: AppContext):
}
async function updateNames(k: number, d: string, n: Record<string, boolean>, c: AppContext): Promise<NostrEvent> {
const signer = new AdminSigner();
const signer = Conf.signer;
const admin = await signer.getPublicKey();
return updateAdminEvent(

View file

@ -20,7 +20,7 @@ export async function getClientConnectUri(signal?: AbortSignal): Promise<string>
url: Conf.localDomain,
};
uri.host = Conf.pubkey;
uri.host = await Conf.signer.getPublicKey();
uri.searchParams.set('relay', Conf.relay);
uri.searchParams.set('metadata', JSON.stringify(metadata));

View file

@ -18,7 +18,7 @@ export interface InstanceMetadata extends NostrMetadata {
/** Get and parse instance metadata from the kind 0 of the admin user. */
export async function getInstanceMetadata(store: NStore, signal?: AbortSignal): Promise<InstanceMetadata> {
const [event] = await store.query(
[{ kinds: [0], authors: [Conf.pubkey], limit: 1 }],
[{ kinds: [0], authors: [await Conf.signer.getPublicKey()], limit: 1 }],
{ signal },
);

View file

@ -57,7 +57,7 @@ export async function localNip05Lookup(store: NStore, localpart: string): Promis
const [grant] = await store.query([{
kinds: [30360],
'#d': [`${localpart}@${Conf.url.host}`],
authors: [Conf.pubkey],
authors: [await Conf.signer.getPublicKey()],
limit: 1,
}]);

View file

@ -6,7 +6,7 @@ export async function getRelays(store: NStore, pubkey: string): Promise<Set<stri
const relays = new Set<`wss://${string}`>();
const events = await store.query([
{ kinds: [10002], authors: [pubkey, Conf.pubkey], limit: 2 },
{ kinds: [10002], authors: [pubkey, await Conf.signer.getPublicKey()], limit: 2 },
]);
for (const event of events) {

View file

@ -2,11 +2,11 @@ import { NSchema as n, NStore } from '@nostrify/nostrify';
import { Conf } from '@/config.ts';
import { configSchema } from '@/schemas/pleroma-api.ts';
import { AdminSigner } from '@/signers/AdminSigner.ts';
import { PleromaConfigDB } from '@/utils/PleromaConfigDB.ts';
export async function getPleromaConfigs(store: NStore, signal?: AbortSignal): Promise<PleromaConfigDB> {
const { pubkey } = Conf;
const signer = Conf.signer;
const pubkey = await signer.getPublicKey();
const [event] = await store.query([{
kinds: [30078],
@ -20,7 +20,7 @@ export async function getPleromaConfigs(store: NStore, signal?: AbortSignal): Pr
}
try {
const decrypted = await new AdminSigner().nip44.decrypt(Conf.pubkey, event.content);
const decrypted = await signer.nip44.decrypt(pubkey, event.content);
const configs = n.json().pipe(configSchema.array()).catch([]).parse(decrypted);
return new PleromaConfigDB(configs);
} catch (_e) {

View file

@ -1,4 +1,3 @@
import { AdminSigner } from '@/signers/AdminSigner.ts';
import { Conf } from '@/config.ts';
import { NSchema as n, NStore } from '@nostrify/nostrify';
import { nostrNow } from '@/utils.ts';
@ -38,13 +37,13 @@ export async function getZapSplits(store: NStore, pubkey: string): Promise<Ditto
}
export async function seedZapSplits(store: NStore) {
const zapSplit: DittoZapSplits | undefined = await getZapSplits(store, Conf.pubkey);
const zapSplit: DittoZapSplits | undefined = await getZapSplits(store, await Conf.signer.getPublicKey());
if (!zapSplit) {
const dittoPubkey = '781a1527055f74c1f70230f10384609b34548f8ab6a0a6caa74025827f9fdae5';
const dittoMsg = 'Official Ditto Account';
const signer = new AdminSigner();
const signer = Conf.signer;
const event = await signer.signEvent({
content: '',
created_at: nostrNow(),

View file

@ -10,7 +10,7 @@ interface RenderNotificationOpts {
viewerPubkey: string;
}
function renderNotification(event: DittoEvent, opts: RenderNotificationOpts) {
async function renderNotification(event: DittoEvent, opts: RenderNotificationOpts) {
const mentioned = !!event.tags.find(([name, value]) => name === 'p' && value === opts.viewerPubkey);
if (event.kind === 1 && mentioned) {
@ -29,7 +29,7 @@ function renderNotification(event: DittoEvent, opts: RenderNotificationOpts) {
return renderReaction(event, opts);
}
if (event.kind === 30360 && event.pubkey === Conf.pubkey) {
if (event.kind === 30360 && event.pubkey === await Conf.signer.getPublicKey()) {
return renderNameGrant(event);
}

View file

@ -48,7 +48,7 @@ class PolicyWorker implements NPolicy {
await this.worker.init({
path: Conf.policy,
databaseUrl: Conf.databaseUrl,
pubkey: Conf.pubkey,
pubkey: await Conf.signer.getPublicKey(),
});
logi({

View file

@ -1,12 +1,12 @@
import { JsonParseStream } from '@std/json/json-parse-stream';
import { TextLineStream } from '@std/streams/text-line-stream';
import { AdminSigner } from '../packages/ditto/signers/AdminSigner.ts';
import { Conf } from '../packages/ditto/config.ts';
import { Storages } from '../packages/ditto/storages.ts';
import { type EventStub } from '../packages/ditto/utils/api.ts';
import { nostrNow } from '../packages/ditto/utils.ts';
const signer = new AdminSigner();
const signer = Conf.signer;
const store = await Storages.db();
const readable = Deno.stdin.readable

View file

@ -1,7 +1,7 @@
import { NSchema } from '@nostrify/nostrify';
import { nip19 } from 'nostr-tools';
import { AdminSigner } from '../packages/ditto/signers/AdminSigner.ts';
import { Conf } from '../packages/ditto/config.ts';
import { Storages } from '../packages/ditto/storages.ts';
import { nostrNow } from '../packages/ditto/utils.ts';
@ -20,7 +20,7 @@ if (!['admin', 'user'].includes(role)) {
Deno.exit(1);
}
const signer = new AdminSigner();
const signer = Conf.signer;
const admin = await signer.getPublicKey();
const [existing] = await store.query([{

View file

@ -1,7 +1,6 @@
import { Command } from 'commander';
import { NostrEvent } from 'nostr-tools';
import { AdminSigner } from '../packages/ditto/signers/AdminSigner.ts';
import { nostrNow } from '../packages/ditto/utils.ts';
import { Conf } from '../packages/ditto/config.ts';
import { Storages } from '../packages/ditto/storages.ts';
@ -36,7 +35,7 @@ if (import.meta.main) {
content.picture = image;
content.website = Conf.localDomain;
const signer = new AdminSigner();
const signer = Conf.signer;
const bare: Omit<NostrEvent, 'id' | 'sig' | 'pubkey'> = {
created_at: nostrNow(),
kind: 0,