---
name: discord-channel-access
description: Set a Discord channel to tag-required or tag-free for an oracle. Minimal, code-first, no narrative. Covers both directions (requireMention true/false), the two footguns, and the mandatory restart. Portable to any oracle repo.
installer: create-shortcut
created_at: 2026-08-13T15:44:00+07:00
---

# /discord-channel-access

Set a Discord channel to **tag-required** or **tag-free**. Run top to bottom.

```bash
CH=<channel-id>          # right-click channel → Copy Channel ID (needs Developer Mode)
ORACLE=<oracle-name>
MODE=<open|tagonly>      # open = no @mention needed, tagonly = must @mention

# 0. path check — fail loud if wrong repo
ORACLE_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
[ -f "$ORACLE_ROOT/CLAUDE.md" ] && [ -d "$ORACLE_ROOT/.discord" ] \
  && echo "OK: $ORACLE_ROOT" || { echo "FAIL: wrong repo"; exit 1; }
F="$ORACLE_ROOT/.discord/access.json"

# 0.5. oracle check — is it even running? (edit still valid, just won't apply until wake)
maw locate "$ORACLE" 2>&1 | grep -E "session:|node:"

# 1. current state
jq --arg ch "$CH" '.groups[$ch] // "NOT SET (drops silently)"' "$F"

# 2. set — atomic write, tmp then mv
REQ=$([ "$MODE" = tagonly ] && echo true || echo false)
jq --arg ch "$CH" --argjson req "$REQ" '.groups[$ch].requireMention = $req' "$F" > "$F.tmp" && mv "$F.tmp" "$F"

# 3. confirm — usually NO restart needed (see below)
jq --arg ch "$CH" '.groups[$ch]' "$F"
```

## restart: usually NOT needed — CORRECTED 2026-08-13

⚠️ An earlier version of this skill said restart was mandatory. That was
**wrong for `access.json` content edits**, confirmed by reading the live
plugin source (`~/.claude/plugins/cache/claude-plugins-official/discord/0.0.4/server.ts`):

```ts
function loadAccess(): Access { return BOOT_ACCESS ?? readAccessFile() }
```

`loadAccess()` is called **fresh, from 5 separate call sites**, on every
message check — no caching. `readAccessFile()` does a real `readFileSync` each
time. **So editing `groups.<id>.requireMention` or `allowFrom` hot-reloads —
no restart needed.** Just send a real test message and watch for a reply.

**The one thing that IS fixed at process boot** (restart genuinely required):
`DISCORD_STATE_DIR` itself — line 37, `process.env.DISCORD_STATE_DIR ?? join(homedir(), '.claude', 'channels', 'discord')`,
read once at module load. Changing *which directory* the plugin reads from
needs a restart. Editing the file *inside* that directory does not.

**The one exception to hot-reload**: `DISCORD_ACCESS_MODE=static` snapshots
access at boot (`BOOT_ACCESS`) and never re-reads — if that env var is set,
you're back to needing a restart. Check: `grep DISCORD_ACCESS_MODE .discord/.env`
(or wherever this oracle's env lives).

Credit: this correction came from an independent gist
(`gist.github.com/nazt/0454dbccc7962d7119fd253af422743e`) that made the
opposite claim from this skill's first version — verifying against the real
source settled it in the gist's favor.

## ⚠️ footguns

| state | meaning |
|---|---|
| no entry for the channel | **every message dropped silently** — not an error |
| `"allowFrom": []` (empty array) | **open to everyone** — not "nobody" |
| `"allowFrom"` key omitted | open to everyone |
| `"allowFrom": ["<id>"]` | only that user ID |

## restart is not optional

Editing `access.json` while the process is running does nothing until restart.
Root cause: dig #178 ("marker lies") — config reads once at boot, stays stale
in memory forever otherwise.

## if the oracle isn't on Discord at ALL

Different problem — see `/discord-online`
(`laris-co/artifacts-oracle/.claude/skills/discord-online/SKILL.md`, 7-layer
preflight: marketplace path, bun, `DISCORD_STATE_DIR`, stale lock,
`enabledPlugins`, literal `~`, direnv scope).

## hand to another oracle

> Read `digger-oracle/skills/discord-channel-access/SKILL.md`, set channel
> `<channel-id>` to `<open|tagonly>` for yourself, confirm with a real test
> message, report back.
