feat: make lock event hashtag configurable via config and env var

- Added `getHashtag` function to `src/torch-config.mjs` to resolve hashtag from `NOSTR_LOCK_HASHTAG` env var, `dashboard.hashtag` config, or default to `${namespace}-agent-lock`.
- Updated `src/lib.mjs` to use `getHashtag()` in `cmdLock` and `cmdComplete` for publishing events.
- Updated `dashboard/index.html` to remove hardcoded `#torch-agent-lock` string and rely on dynamic configuration.
- Updated `src/docs/TORCH.md` and `README.md` to document the new configuration options.
- Verified changes with unit tests and frontend verification script.

Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
google-labs-jules[bot]andPR0M3TH3AN committed 2026-02-15 18:54:46 +00:00
1 parent f279e9ae95
commit cbc5054b94
5 files changed
+27 -3

No files matched your search

+2
View File
@@ -71,6 +71,7 @@ Configuration options:
- `nostrLock.dailyRoster` / `nostrLock.weeklyRoster` (explicit rosters) - `nostrLock.dailyRoster` / `nostrLock.weeklyRoster` (explicit rosters)
- `dashboard.defaultCadenceView` (`daily`, `weekly`, or `all`) - `dashboard.defaultCadenceView` (`daily`, `weekly`, or `all`)
- `dashboard.defaultStatusView` (`active` or `all`) - `dashboard.defaultStatusView` (`active` or `all`)
- `dashboard.hashtag` (custom hashtag for lock events)
- `scheduler.firstPromptByCadence.daily` / `.weekly` (first prompt to run) - `scheduler.firstPromptByCadence.daily` / `.weekly` (first prompt to run)
- `scheduler.paused.daily` / `.weekly` (array of agent names to pause locally) - `scheduler.paused.daily` / `.weekly` (array of agent names to pause locally)
@@ -88,6 +89,7 @@ npx --no-install torch-lock dashboard --port 4173
You can override configuration using environment variables: You can override configuration using environment variables:
- `NOSTR_LOCK_NAMESPACE` - `NOSTR_LOCK_NAMESPACE`
- `NOSTR_LOCK_HASHTAG`
- `NOSTR_LOCK_RELAYS` (comma-separated) - `NOSTR_LOCK_RELAYS` (comma-separated)
- `NOSTR_LOCK_TTL` - `NOSTR_LOCK_TTL`
- `NOSTR_LOCK_QUERY_TIMEOUT_MS` - `NOSTR_LOCK_QUERY_TIMEOUT_MS`
+1 -1
View File
@@ -37,7 +37,7 @@
> >
lock events published by AI agents working on this TORCH project. Events lock events published by AI agents working on this TORCH project. Events
are kind <code class="mono text-xs">30078</code> filtered by the are kind <code class="mono text-xs">30078</code> filtered by the
<code id="introHashtag" class="mono text-xs">#torch-agent-lock</code> hashtag on public <code id="introHashtag" class="mono text-xs">...</code> hashtag on public
Nostr relays. Nostr relays.
</p> </p>
</header> </header>
+2
View File
@@ -44,6 +44,7 @@ node bin/torch-lock.mjs health --cadence daily
## Environment variables ## Environment variables
- `NOSTR_LOCK_NAMESPACE` - `NOSTR_LOCK_NAMESPACE`
- `NOSTR_LOCK_HASHTAG`
- `NOSTR_LOCK_RELAYS` - `NOSTR_LOCK_RELAYS`
- `NOSTR_LOCK_TTL` - `NOSTR_LOCK_TTL`
- `NOSTR_LOCK_QUERY_TIMEOUT_MS` - `NOSTR_LOCK_QUERY_TIMEOUT_MS`
@@ -77,6 +78,7 @@ Common settings:
- `nostrLock.dailyRoster` / `nostrLock.weeklyRoster` — optional per-project roster overrides. - `nostrLock.dailyRoster` / `nostrLock.weeklyRoster` — optional per-project roster overrides.
- `dashboard.defaultCadenceView` — default dashboard view (`daily`, `weekly`, `all`). - `dashboard.defaultCadenceView` — default dashboard view (`daily`, `weekly`, `all`).
- `dashboard.defaultStatusView` — default dashboard status filter (`active`, `all`). - `dashboard.defaultStatusView` — default dashboard status filter (`active`, `all`).
- `dashboard.hashtag` — custom hashtag for lock events (defaults to `<namespace>-agent-lock`).
- `scheduler.firstPromptByCadence.daily` / `.weekly` — first-run scheduler starting agent. - `scheduler.firstPromptByCadence.daily` / `.weekly` — first-run scheduler starting agent.
- `scheduler.handoffCommandByCadence.daily` / `.weekly` — shell command run after lock acquisition; command must use `SCHEDULER_AGENT`, `SCHEDULER_CADENCE`, and `SCHEDULER_PROMPT_PATH` provided by `scripts/agent/run-scheduler-cycle.mjs`. - `scheduler.handoffCommandByCadence.daily` / `.weekly` — shell command run after lock acquisition; command must use `SCHEDULER_AGENT`, `SCHEDULER_CADENCE`, and `SCHEDULER_PROMPT_PATH` provided by `scripts/agent/run-scheduler-cycle.mjs`.
- `scheduler.paused.daily` / `.weekly` — array of agent names to exclude from scheduler rotation. - `scheduler.paused.daily` / `.weekly` — array of agent names to exclude from scheduler rotation.
+9 -2
View File
@@ -11,6 +11,7 @@ import {
getRelays as _getRelays, getRelays as _getRelays,
getNamespace as _getNamespace, getNamespace as _getNamespace,
getTtl as _getTtl, getTtl as _getTtl,
getHashtag as _getHashtag,
} from './torch-config.mjs'; } from './torch-config.mjs';
import { import {
DEFAULT_DASHBOARD_PORT, DEFAULT_DASHBOARD_PORT,
@@ -191,6 +192,7 @@ export async function cmdLock(agent, cadence, dryRun = false, deps = {}) {
const { const {
getRelays = _getRelays, getRelays = _getRelays,
getNamespace = _getNamespace, getNamespace = _getNamespace,
getHashtag = _getHashtag,
getTtl = _getTtl, getTtl = _getTtl,
queryLocks = _queryLocks, queryLocks = _queryLocks,
getRoster = _getRoster, getRoster = _getRoster,
@@ -206,12 +208,14 @@ export async function cmdLock(agent, cadence, dryRun = false, deps = {}) {
const relays = getRelays(); const relays = getRelays();
const namespace = getNamespace(); const namespace = getNamespace();
const hashtag = getHashtag();
const dateStr = getDateStr(); const dateStr = getDateStr();
const ttl = getTtl(); const ttl = getTtl();
const now = nowUnix(); const now = nowUnix();
const expiresAt = now + ttl; const expiresAt = now + ttl;
error(`Locking: namespace=${namespace}, agent=${agent}, cadence=${cadence}, date=${dateStr}`); error(`Locking: namespace=${namespace}, agent=${agent}, cadence=${cadence}, date=${dateStr}`);
error(`Hashtag: #${hashtag}`);
error(`TTL: ${ttl}s, expires: ${new Date(expiresAt * 1000).toISOString()}`); error(`TTL: ${ttl}s, expires: ${new Date(expiresAt * 1000).toISOString()}`);
error(`Relays: ${relays.join(', ')}`); error(`Relays: ${relays.join(', ')}`);
@@ -259,7 +263,7 @@ export async function cmdLock(agent, cadence, dryRun = false, deps = {}) {
created_at: now, created_at: now,
tags: [ tags: [
['d', `${namespace}-lock/${cadence}/${agent}/${dateStr}`], ['d', `${namespace}-lock/${cadence}/${agent}/${dateStr}`],
['t', `${namespace}-agent-lock`], ['t', hashtag],
['t', `${namespace}-lock-${cadence}`], ['t', `${namespace}-lock-${cadence}`],
['t', `${namespace}-lock-${cadence}-${dateStr}`], ['t', `${namespace}-lock-${cadence}-${dateStr}`],
['expiration', String(expiresAt)], ['expiration', String(expiresAt)],
@@ -313,6 +317,7 @@ export async function cmdLock(agent, cadence, dryRun = false, deps = {}) {
log(`LOCK_EVENT_ID=${event.id}`); log(`LOCK_EVENT_ID=${event.id}`);
log(`LOCK_PUBKEY=${pk}`); log(`LOCK_PUBKEY=${pk}`);
log(`LOCK_AGENT=${agent}`); log(`LOCK_AGENT=${agent}`);
log(`LOCK_HASHTAG=${hashtag}`);
log(`LOCK_CADENCE=${cadence}`); log(`LOCK_CADENCE=${cadence}`);
log(`LOCK_DATE=${dateStr}`); log(`LOCK_DATE=${dateStr}`);
log(`LOCK_EXPIRES=${expiresAt}`); log(`LOCK_EXPIRES=${expiresAt}`);
@@ -419,6 +424,7 @@ export async function cmdComplete(agent, cadence, dryRun = false, deps = {}) {
const { const {
getRelays = _getRelays, getRelays = _getRelays,
getNamespace = _getNamespace, getNamespace = _getNamespace,
getHashtag = _getHashtag,
queryLocks = _queryLocks, queryLocks = _queryLocks,
publishLock = _publishLock, publishLock = _publishLock,
generateSecretKey = _generateSecretKey, generateSecretKey = _generateSecretKey,
@@ -431,6 +437,7 @@ export async function cmdComplete(agent, cadence, dryRun = false, deps = {}) {
const relays = getRelays(); const relays = getRelays();
const namespace = getNamespace(); const namespace = getNamespace();
const hashtag = getHashtag();
const dateStr = getDateStr(); const dateStr = getDateStr();
const now = nowUnix(); const now = nowUnix();
@@ -466,7 +473,7 @@ export async function cmdComplete(agent, cadence, dryRun = false, deps = {}) {
created_at: now, created_at: now,
tags: [ tags: [
['d', `${namespace}-lock/${cadence}/${agent}/${dateStr}`], ['d', `${namespace}-lock/${cadence}/${agent}/${dateStr}`],
['t', `${namespace}-agent-lock`], ['t', hashtag],
['t', `${namespace}-lock-${cadence}`], ['t', `${namespace}-lock-${cadence}`],
['t', `${namespace}-lock-${cadence}-${dateStr}`], ['t', `${namespace}-lock-${cadence}-${dateStr}`],
// No expiration tag -> permanent // No expiration tag -> permanent
+13
View File
@@ -282,3 +282,16 @@ export function getMinActiveRelayPool() {
assertPositiveCount(value, 'effective min active relay pool'); assertPositiveCount(value, 'effective min active relay pool');
return value; return value;
} }
export function getHashtag() {
const config = loadTorchConfig();
const envValue = process.env.NOSTR_LOCK_HASHTAG;
if (envValue) {
return envValue.trim();
}
if (config.dashboard.hashtag) {
return config.dashboard.hashtag;
}
const namespace = getNamespace();
return `${namespace}-agent-lock`;
}