Skip to content

Key Management Guide

DiscussionBridge uses Discourse API keys to publish, sync, diagnose, and recover companion topics. Treat keys as operational credentials.

Use separate keys when possible:

  • publishing key: normal publish-new, sync-existing, and publish-and-sync runs
  • diagnostics key: setup checks with check-discourse
  • recovery/admin key: rare repair operations that need broader authority

For Alpha, the practical model is:

  • use a granular publishing key when it can create topics/posts, read linked topics/posts, update managed first posts, update topic metadata, update tags, and unlist when needed
  • use a global/admin-capable diagnostics key when granular keys cannot read the site metadata or reconciliation endpoints needed by check-discourse

Established product rule: when documenting or requesting a granular publishing key, provide the exact Discourse scope settings. Do not describe the publishing key as “similar”, “roughly”, or “mostly” once a product key model has been settled.

Discourse API keys have two independent controls:

  • User Level: All Users or Single User;
  • Scope: Global, Read-only, or Granular.

With All Users, Discourse allows the request to act on behalf of the username sent in Api-Username. With Single User, the key is bound to the selected user. Scope separately controls which endpoints/actions are available. See Discourse Meta: Create and configure an API key.

Discussion Bridge now has preferred request-actor controls:

  • CLI --post-as;
  • environment DISCOURSE_POST_AS;
  • publishOnBuild lane/default postAs or postAsEnv.

Legacy --api-username, DISCOURSE_API_USERNAME, apiUsername, and apiUsernameEnv remain compatible fallbacks. Actor precedence is explicit/lane postAs, named postAsEnv, DISCOURSE_POST_AS, then legacy API-username controls. The selected actor is sent as Discourse Api-Username.

When Discourse supports or confirms granular diagnostics/read scopes for the required endpoints, move to a two-key model:

  • granular publishing key for runtime sync
  • granular diagnostics/read key for setup checks

The publishing key needs enough permission to:

  • create topics and posts in the target category
  • read existing linked topics and posts
  • update the managed first post
  • update topic title/category/tag metadata when enabled
  • apply tags or create tags when the lane requires it
  • unlist topics when --unlist is used

On typical Discourse installs, retitling replied topics and changing listing status can require a staff or moderator-capable user.

Use this exact granular publishing/runtime key scope set unless a new feature explicitly requires a documented change:

categories
list
show
tags
list
topics
write
update
read
status
posts
edit
list
search
show

This is the settled runtime publishing key model used for Discussion Bridge-style publishing and sync. Diagnostics/setup work stays on the diagnostics key.

Allowed parameters from the Discourse granular key UI:

categories show
id: any parameter
posts edit
id: any parameter
search show
q: any parameter
page: any parameter
topics write
topic_id: any parameter
topics update
topic_id: any parameter
category_id: any parameter
topics read
topic_id: any parameter
external_id: any parameter
topics status
topic_id: any parameter
category_id: any parameter
status: any parameter
enabled: any parameter

check-discourse is read-only, but useful diagnostics may require endpoints that granular keys cannot always read.

It may inspect:

  • /site/settings.json for client-visible authoring limits
  • /site.json for user-specific capabilities such as tag permissions
  • /categories.json for category existence
  • /tags.json for tag inventory
  • /embed/info?embed_url=... for existing embedded-topic reconciliation
  • exact URL search as a fallback reconciliation check

If a granular key receives 403 for these endpoints, use one of these fallbacks:

  • provide explicit CLI/env limits for title/body/tag preflight
  • run check-discourse with a diagnostics key
  • manually verify the Discourse setting and record it in deployment docs

Runtime publishing:

Terminal window
DISCOURSE_POST_AS=discussbridge-bot
DISCOURSE_API_USERNAME=discussbridge-bot
DISCOURSE_API_KEY=publishing-key

Prefer DISCOURSE_POST_AS. Keep DISCOURSE_API_USERNAME only where a current installation still relies on the compatibility fallback.

Optional diagnostics:

Terminal window
DISCOURSE_DIAGNOSTICS_API_KEY=diagnostics-key

Optional explicit limits:

Terminal window
DISCOURSE_TITLE_MIN_LENGTH=15
DISCOURSE_MAX_TOPIC_TITLE_LENGTH=255
DISCOURSE_MAX_POST_LENGTH=32000
DISCOURSE_MAX_TAGS_PER_TOPIC=5
DISCOURSE_MAX_TAG_LENGTH=20

Do:

  • store keys in a protected credential vault
  • store deployment secrets in the hosting provider’s encrypted environment settings
  • keep local shell variables session-scoped when testing
  • rotate keys after accidental exposure
  • keep key filenames descriptive enough to identify purpose and date

Do not:

  • commit API keys
  • paste real keys into docs, issues, PRs, screenshots, or build logs
  • put production keys in example .env files
  • reuse personal admin keys for routine automation

Example filename convention:

discussionbridge-forum-discussbridge-bot-publishing-granular-key-YYYYMMDD.txt
discussionbridge-forum-discussbridge-bot-diagnostics-key-YYYYMMDD.txt

Use the same structure for every stored Discourse API key. Keep the human-readable purpose and scope above the key value.

Whenever setup instructions ask an operator to create a key, present the matching metadata block together with the key description, user, scope, and granular permissions. Copy that metadata into the respective protected credential file above the secret value. The manual and setup interaction show the placeholder only; they never show or request the real key.

Purpose: Runtime publishing granular key
Use: publish-new, sync-existing, publish-and-sync, check-discourse basic limits
Bot user role: Admin currently; intended future runtime posture is non-admin or least-privilege
Key user level: Record All Users or Single User; prefer Single User for a fixed runtime actor
Key scope: Granular
Operational rule: Use this to validate the minimum permissions needed for normal bridge publishing.
Description
{site-or-project} publishing granular key
Key selected user
{bot-username when User Level is Single User; not applicable for All Users}
Request actor
{resolved postAs / Api-Username}
User Level
{Single User or All Users; if Single User, record the selected user}
Scope
Granular
Scopes
categories:list
categories:show
posts:edit
posts:list
search:show
tags:list
topics:write
topics:update
topics:read
topics:status
Key
{paste key here}
Purpose: Diagnostics/setup key
Use: check-discourse only
Bot user role: Admin
Key user level: Record All Users or Single User and the selected user/actor relationship
Key scope: Global or admin-read capable
Operational rule: Do not use in CI/build unless explicitly intended
Description
{site-or-project} diagnostics key
Key selected user
{admin-bot-username when User Level is Single User; not applicable for All Users}
Request actor
{resolved postAs / Api-Username used for diagnostics}
User Level
{Single User or All Users; if Single User, record the selected user}
Scope
Global or admin-read capable
Permissions
check-discourse only; global or admin-read capability as required by setup checks
Key
{paste key here}

Keys can leak through:

  • committed .env files
  • command history copied into issues or chat
  • build logs that print environment variables
  • screenshots of terminal windows or admin pages
  • CI/CD secret misconfiguration
  • shared machines or synced folders with broad access
  • package fixtures or demo repos that accidentally include real credentials

If a key leaks, revoke it in Discourse, create a replacement, update the deployment secret, and rerun check-discourse.

Rotate keys when:

  • a user leaves the project
  • a key is pasted into an unsafe place
  • permissions change from global to granular
  • the bot user’s role changes
  • moving from Alpha testing to a public release

For rotation:

  1. Create the replacement key.
  2. Update local or hosting secrets.
  3. Run check-discourse.
  4. Run a dry-run publish or sync.
  5. Revoke the old key.
  6. Record the rotation date in private ops notes.

postAs is now the implemented preferred actor selector. It changes the Api-Username request actor; it does not transfer ownership of an existing topic. Whether that actor may differ from the key’s selected user depends on the key’s User Level, while endpoint authority depends on Scope.

Before a write, record and verify the key User Level, its selected user when Single User, the resolved postAs/Api-Username actor, the key Scope/granular permissions, and the actor’s category/tag/staff permissions. Live CLI output should show Post as: USER; check-discourse reports Request actor.

Across connected forums, no two nonhuman accounts may have the same—or visually ambiguous—username/display-name combination. Encode both role and origin system.

Origin Candidate username Candidate public name
Discussion Bridge Forum editorbridgeforum Discussion Bridge Forum Editor
Citizen Activist Network Forum editorcanforum CAN Forum Editor

Discourse normalizes username case. Check the exact final username against each site’s length rules and availability before creation. Astro-origin publishing accounts should identify the originating site/brand rather than only the destination forum. Preserve established obbba-bot as the OBBBA source identity.

Create a special-admin custom group on each forum as the visible inventory home for nonhuman admin/service accounts. Group membership is organizational only: it does not grant Discourse admin status, category permissions, or API rights. Assign those separately.

The CLI should never print key values. Operators should also avoid commands that echo environment variables near build output.

Safe:

Terminal window
npx astro-discussion-bridge check-discourse --discourse-url https://forum.example.com --category-id 5

Unsafe:

Terminal window
echo $DISCOURSE_API_KEY

When reporting support issues, include command, mode, Discourse URL, category ID, tags, page URL, and sanitized error output. Do not include API key values.