Skip to content

Security and Secrets

This page covers safe handling for server keys, global authorization keys, webhook signatures, and logs.

Key Types

Key Sent as Use
Server key Server-Key Required for server API requests.
Global key Authorization Optional PRC large-app authorization flow.

Create clients with explicit values or environment variables:

import os
from erlc_api import AsyncERLC

api = AsyncERLC(
    os.environ["ERLC_SERVER_KEY"],
    global_key=os.environ.get("ERLC_GLOBAL_KEY"),
)

Secret Storage

Prefer environment variables, deployment secrets, or your platform secret store. Do not commit server keys to source control.

Suggested environment names:

ERLC_SERVER_KEY
ERLC_GLOBAL_KEY
ERLC_WEBHOOK_SECRET_CONTEXT

The wrapper does not require these exact names; they are conventions for your application.

Logging Redaction

Do not log:

  • Server-Key;
  • Authorization;
  • raw request headers;
  • full request objects from frameworks;
  • webhook raw bodies when they may include private moderation text.

Log safe context instead:

try:
    await api.players()
except Exception as exc:
    logger.warning("ERLC request failed: %s", exc)

Wrapper exceptions expose short body excerpts and request metadata without requiring secret headers.

When you need to correlate logs with a configured key, log a fingerprint instead of the key:

from erlc_api.security import key_fingerprint

logger.info("Configured ERLC server key %s", key_fingerprint(server_key))

Fingerprints are one-way hash prefixes. They are useful for diagnostics, but they are not proof that a key is valid.

Setup Validation

Validate keys during setup or bot startup before enabling command routes:

from erlc_api import AsyncERLC, ValidationStatus

api = AsyncERLC(server_key)

async with api:
    result = await api.validate_key()
    if result.status is not ValidationStatus.OK:
        raise RuntimeError(f"ERLC key validation failed: {result.status}")

If a key repeatedly returns auth failures, stop using that configured server, disable command routes for it, and ask an operator to rotate or re-enter the key. If your bot leaves or is removed from a Discord guild, remove that guild's stored server key from your own storage.

The HTTP layer tracks repeated auth failures in memory by key fingerprint only:

from erlc_api.security import auth_failures

for record in auth_failures.snapshot():
    if record.repeated:
        logger.warning("Repeated ERLC auth failures for %s", record.fingerprint)

This is process-local guidance. It does not block keys, store raw secrets, or replace your own account/guild configuration lifecycle.

Webhook Verification

Verify the raw request body before trusting JSON:

from erlc_api.webhooks import WebhookError, assert_valid_event_webhook_signature


async def handler(request):
    raw_body = await request.body()
    try:
        assert_valid_event_webhook_signature(raw_body=raw_body, headers=request.headers)
    except WebhookError:
        return {"ok": False}, 401

    payload = await request.json()
    return {"ok": True, "payload": payload}

Important rules:

  • Use the exact raw bytes from the request.
  • Do not reserialize JSON before verification.
  • Reject invalid signatures with a non-2xx response.
  • Keep timestamp skew checks enabled unless your deployment requires otherwise.

Command Safety

The wrapper normalizes command syntax, but it does not invent PRC permissions or Discord role policies. Put application-level checks in your bot, web route, middleware, or custom command predicate.

from erlc_api import CommandPolicy

announce_policy = CommandPolicy(allowed={"h"}, max_length=120)


@router.command("announce", predicate=lambda _invocation, ctx: ctx.arg(0) is not None)
async def announce(ctx):
    command_text = announce_policy.validate(f"h {ctx.rest()}")
    ...

Common Mistakes

  • Printing api objects or request headers in logs.
  • Verifying webhook signatures after parsing JSON.
  • Treating global_key as a replacement for server_key.
  • Building a public web endpoint that executes commands without app-level auth.

Previous Page: Custom Commands Reference | Next Page: Rate Limits, Retries, and Reliability