Skip to content

feat(auth): add sender admission overlays and /user commands - #223

Merged
BegoniaHe merged 7 commits into
masterfrom
feat/sender-admission-overlays
Sep 18, 2026
Merged

BegoniaHe merged 7 commits into
masterfrom
feat/sender-admission-overlays

Conversation

@BegoniaHe

Copy link
Copy Markdown
Collaborator

Summary

Persist and enforce UID sender overlays (blocked, llm_enabled) and add /user commands that write them. Admission now loads session plus sender overlays, drops blocked senders except recovery commands, and honors admission.unlisted_senders (default allow).

Related issue

Fixes #216

Behavior

  • Sender overlays live on scope=sender, scope_id=Subject.im.id, key session_service_config, and only store blocked / llm_enabled.
  • AdmissionCheckStage loads UMO + UID. blocked=true drops the event in every session of this bot instance, including shared groups, except /user unblock and /bot status.
  • admission.unlisted_senders defaults to allow. deny admits only senders that already have a blocked or llm_enabled overlay.
  • Session llm_enabled=false plus sender llm_enabled=true admits built-in LLM for that sender; the reverse skips LLM while commands still run. A fully blocked session is not revived by a sender VIP overlay.
  • /user block|unblock <sender_id> and /user llm on|off <sender_id> require session.manage and a sender token (raw platform id minted like /admin grant, or a full im: id). Empty token prints usage and writes nothing.

Non-goals

Dashboard custom-rules sender picker and OpenAPI generation (A4 / #217). Expiry, UMO×UID third selector, LLM blacklist tool, /black. Absorbing SessionStatusCheckStage into Admission. Writing persona / KB / TTS / Provider onto sender rows. Changing provider.manage bypass, webchat skip, or notice/request skip.

Implementation notes

Surgical current-path: reuse A1 compose_admission() and A2 AdmissionCheckStage. Sender writes go through sender_service_config() so extra keys are dropped. should_process_llm_request still composes llm_enabled only; blocked is an Admission event drop. OpenAPI generation is not required.

Four commits on feat/sender-admission-overlays: persist setters, admission execution, /user commands, bilingual docs.

Validation

uv run ruff format <changed python>
uv run ruff check <changed python>
uv run pytest tests/unit/test_admission.py tests/unit/test_admission_check_stage.py tests/unit/test_session_llm_manager.py tests/unit/test_session_status_check_stage.py tests/unit/test_agent_request_sub_stage.py tests/unit/test_builtin_command_extensions.py tests/unit/test_config.py tests/unit/test_config_metadata_i18n.py
cd dashboard && pnpm i18n:check
cd docs && pnpm run docs:build
node node_modules/prettier/bin/prettier.cjs --write --ignore-path .gitignore <docs + i18n>
make check-md

Focused pytest passed (202 tests). Ruff on changed Python, Dashboard pnpm i18n:check, docs pnpm run docs:build, Prettier on the touched docs/i18n, and make check-md passed. make check and the full blocking pytest suite were not run.

Compatibility and risk

Not a breaking API change. Default unlisted_senders=allow preserves A2 behavior for unlisted people. Operators who set deny without writing sender overlays will drop those senders. Blocking a sender also blocks them in every session of this instance; recovery is /user unblock and /bot status only. Private-chat session keys and sender keys stay distinct.

Checklist

  • A Feature request Issue exists for large work, or this is a small, obvious addition.
  • The change is focused and does not include unrelated refactoring.
  • I added or updated tests, or explained why tests are not practical.
  • User-visible behavior updates both docs/zh/ and docs/en/.
  • OpenAPI, generated client, docs/public/openapi.json, and tests change together when routes or schemas change.
  • No secrets committed. Runtime Python deps update pyproject.toml, requirements.txt, and uv.lock together.
  • I did not restore legacy shims, Python <3.14 fallbacks, or upstream publish/docs URLs as fork artifacts.
  • Breaking API or behavior changes use ! and a BREAKING CHANGE: footer.
  • I will not merge this PR myself. Merge needs a human maintainer review plus a separate AI-assisted review (AI_POLICY.md).
  • AI use follows AI_POLICY.md. Keep exactly one author note below. Do not fabricate the other.

Agent note

Goal: land A3 of #212 / #216 — persist and execute UID blocked/llm_enabled overlays, add /user writers, and document instance-global sender admission. Four commits on feat/sender-admission-overlays: feat(auth) persist, feat(auth) admission, feat(commands) /user, docs. Checks actually run are listed above; full make check / blocking pytest were not. Residual risk: Dashboard sender list picker remains #217; unlisted_senders=deny has no UI list in this slice; missing /user args fail at command bind rather than the usage i18n path (whitespace tokens do). Tools: OpenCode. This write-up is not the required separate AI-assisted review.

Add sender_get/sender_put and setters that write scope=sender
session_service_config with only blocked and llm_enabled.

Related: #216
AI-Generated: true
Generated-At: 2026-09-17T21:19:23Z
Load UID overlays in AdmissionCheckStage, honor unlisted_senders,
and pass through /user unblock plus /bot status for blocked senders.

Related: #216
AI-Generated: true
Generated-At: 2026-09-17T21:19:38Z
Write blocked and llm_enabled on Subject.im.id with session.manage.
Require a sender token; do not add /black or an LLM tool.

Related: #216
AI-Generated: true
Generated-At: 2026-09-17T21:19:45Z
Record UID overlay semantics, /user commands, and
admission.unlisted_senders on the bilingual use pages.

Related: #216
AI-Generated: true
Generated-At: 2026-09-17T21:19:53Z
Drop the astrbot.core import that failed the builtin-commands
boundary test, print usage for omitted sender tokens, and cover
sender preference helpers.

Related: #216
AI-Generated: true
Generated-At: 2026-09-17T22:38:02Z
Require a single sender id, reject malformed im: keys, and cover
private-chat session/sender key overlap. Drop slice language from
operator-facing docs and config hints.

Related: #216
AI-Generated: true
Generated-At: 2026-09-17T23:09:02Z
@BegoniaHe

Copy link
Copy Markdown
Collaborator Author

Review follow-up on this branch (15e6ddf69).

Issues found and fixed:

  • /user used GreedyStr, so /user block 99 extra wrote sender id 99 extra instead of a bind error.
  • Any im: prefix was stored as-is (im:foo never matched a real Subject.im key). Tokens now have to be im:{platform}:{bot}:{sender}.
  • Issue [feat] add sender admission overlays and /user commands #216 asked for private-chat two-key overlap coverage; session llm_enabled also must not list a sender under unlisted_senders=deny.
  • Operator-facing docs/config hints said “this slice” / “本切片”.

Not changed (intentional for A3 / #217): Dashboard sender picker, sender_remove unlist path, and unlisted_senders=deny bootstrap still needing provider.manage or overlays written while the default is allow.

@xero-team-bot

xero-team-bot Bot commented Sep 17, 2026

Copy link
Copy Markdown

💡 To run commands without mentioning me, run one command with a mention first — @xero-team-bot help — here on this PR. After that, bare commands like yours work without the mention.

Align leftover docs with UID overlays, point /user tokens at
/session info, and cover llm-off admit plus minting.

Related: #216
AI-Generated: true
Generated-At: 2026-09-18T07:45:46Z
@BegoniaHe

Copy link
Copy Markdown
Collaborator Author

AI-assisted review

Reviewed against #216 / A3. No blocking runtime defects. Non-blocking gaps are fixed in 94caf6579.

Blocking

None. Sender overlays persist on scope=sender / Subject.im.id, admission loads UMO+UID, blocked drops instance-wide except /user unblock and /bot status, sender llm_enabled composes per the parent table, and /user writes that row with session.manage. Default unlisted_senders=allow preserves A2 for unlisted people. CI on the previous head was green.

Non-blocking (fixed)

  • Leftover use/dev/faq pages still described admission as session-only.
  • /user copy used get_platform_id() instead of /session info UID / Subject ID.
  • No stage test that llm_enabled=false still admits commands; minting was not locked to sender_admission_key_from_event.
  • Agent-request skip log still said “session disabled” when a sender overlay was the cause.

Intentional leftovers (A4 / #217)

Dashboard sender picker, sender_remove unlist path, and unlisted_senders=deny bootstrap still needing provider.manage or an existing overlay.

Checks run for this follow-up: ruff on the touched Python; focused pytest (235 passed); Prettier on the touched docs/i18n; docs pnpm run docs:build; make check-md-prettier. Full make check / blocking pytest were not re-run here.

@BegoniaHe
BegoniaHe merged commit f2ff832 into master Sep 18, 2026
26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] add sender admission overlays and /user commands

1 participant