Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

:bug: eventing: no owned policy for who may read which alert topic — alert authorization lives in ntfy's auth db

オープン
#899 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 6 日以内に返信

まだ誰も着手していません。

評価

難易度
3/5
見積もり時間
半日
初心者へのやさしさ
62/100
issue の種類
ドキュメント
明瞭さ
おおむね明確
活発さ
活発
技術スタック
python

調査の方向性

Start with DESIGN_PHASE3.md §4.1–§4.3 and §8.7 (the documented-pattern precedent), then skim eventbridge/registry.py's User dataclass and NtfyPublisher to confirm the issue's claims about where authorization does and does not live. The concrete deliverable is the first checkbox: one added paragraph in DESIGN_PHASE3.md §4 stating that alert authorization is ntfy's, not ours, and that per-user revocation does not exist — done when the doc reads true against the code. The second checkbox (a test that provisioned ACLs match the registry, or an explicit note that none exists) is the stretch goal; the third (choosing option A vs B) is a maintainer decision, not yours.

索引モデルが issue の本文から書いたものです。

説明

Summary

There is no policy object, anywhere in this system, that says who may read which
notification topic
. What separates two users' alerts today is possession of an
unguessable topic name, optionally backed by ACLs in ntfy's SQLite auth database,
provisioned by hand with kubectl exec. The authorization decision for alerts is not one
we own, evaluate, or can audit.

This is not a claim that the design is wrong — DESIGN_PHASE3.md §4.1–§4.3 is explicit
that the name is a capability and that auth-default-access: deny-all is "the control
that makes this isolation rather than obscurity". It is a request to record the gap as a
gap, because no document currently states it plainly and the question comes up
immediately when anyone asks how permissions work.

What exists, and what enforces it

Three layers, and they are not equally real:

Layer Where it lives Enforced by
Who may submit work EB_ALLOWED_USERS (env) or EB_USER_REGISTRY_PATH (ConfigMap) our code — ghauth.is_allowed, registry.lookup
Which tenant a request belongs to derived userkey → topics, per-user store our code (resolve_caller, store_registry)
Who may read which alert topic ntfy's auth db ntfy — not us

Verified: grep for ACL / subscription-authorization logic across eventbridge/*.py and
shared/*.py returns nothing. eventbridge/handlers.py has no ntfy references at all.
The only notification-side code is NtfyPublisher, which publishes, and still reads a
single self._cfg.topic for every event — Phase 3 §4.2's per-user _topic_for is design
only.

So on ntfy.sh, the topic name is the entire access control: tenancy.ntfy_topic gives
130 bits of unguessability, and anyone who learns the name reads the stream — the prompt,
the assistant's reply and the stats footer, because compose_body deliberately packs all
of it in so a phone that cannot reach EventBridge still shows the result. §4.1 says this
outright ("the feature that makes the notification good is what makes the leak complete").

What the registry does and does not model

eventbridge/registry.py:64 is the closest thing to a role model:

@dataclass(frozen=True)
class User:
    issuer: str
    userid: str
    userkey: str
    tier: str = TIER_SHARED      # shared | isolated
    agent: str = ""
    limits: Limits = ...

tier is a placement decision (shared vs dedicated runner), not a permission. agent
selects an AgentSpec. limits is rate and size. Nothing expresses "principal P may
read topic T"
— because the model is implicitly one-to-one: a user reads their own
topic and no one else's, and that relation is re-derived from the userkey rather than
stored.

That is a defensible MVP. It becomes insufficient the moment any of these is wanted, and
all three are ordinary asks:

  • An on-call rotation seeing another user's approval requests (directly relevant to
    #897, where a sleeping approver is the designed-for failure case — §6 exists entirely
    because of it).
  • A team topic several people read.
  • Revocation. Today, removing someone's access to alerts means rotating
    EB_NTFY_TOPIC_SECRET_PATH, which rotates every user's topic at once (§4.2 names
    this as a feature for leak response, and it is — but it is the only lever, so
    per-user revocation does not exist).

Why "it's in ntfy's ACLs" is not sufficient as an answer

Even in incluster mode, where ACLs are real:

  1. The grants live outside the declared state. §4.3's own caveat: "Losing the PVC
    loses the ACLs, not just the cache — the auth database lives there."
    k8s_tenant.py apply --all is the recovery path, which makes the registry the source
    of truth for provisioning while the enforcement point is a cache of it that can
    silently drift.
  2. Provisioning is imperative and not idempotent. §4.3: the commands "only work on the
    server itself — hence kubectl exec", and "user add fails if the user exists". The
    declarative alternative is explicitly rejected because removing a user from the config
    file deletes them from the database.
  3. Nothing verifies the grants match the registry. There is no reconcile loop and no
    test. An ACL that was never applied, or applied to the wrong literal topic, fails
    open (the user reads nothing → looks like a broken notification) or, if a prefix
    grant is used by mistake, fails catastrophically (§4.3 warns: a kev1-* read grant
    "would hand them everyone's notifications").
  4. We cannot audit it. There is no endpoint, page or log line that answers "who can
    currently read alice's alerts?" The answer is in a SQLite file in a pod.

Point 3 is the one that matches this repository's own pattern: DESIGN_PHASE2.md §8.5.3
asks what happens when a control is half-configured, and here the half-configured case is
silent, in the direction of either no notifications or everyone's notifications.

Suggested direction (not a prescription)

Two shapes, and they are genuinely different bets:

A. Keep ntfy as the transport, own the policy. Add a notification-access relation to
the registry — subjects, topics, and a verb — and make k8s_tenant.py reconcile ntfy's
ACLs from it rather than emit missing grants once. Add a read-only endpoint that reports
the effective grants so the question is answerable. Cheap, incremental, and leaves
enforcement in ntfy.

B. Stop treating the ntfy topic as the authorization boundary. Notifications are
fetched through EventBridge (which already authenticates, already knows the owner, and is
already the PEP per §2.7), and ntfy carries only a content-free wake-up. Alert access then
becomes an owner-scoped read like every other read in §6.2, enforced by code we test.
Bigger change; removes the whole category.

B composes better with #897's approval flow, because an approval decision is a privileged
action and §5.4 already has to bind it to a user and a tool input — at which point the
notification channel being merely a transport is the simpler story. But A is a real option
and the cheaper one, and the choice should be deliberate rather than defaulted.

What would make this concrete

  • State the gap in DESIGN_PHASE3.md §4 — one paragraph saying alert authorization is
    ntfy's, not ours, and that per-user revocation does not exist. §8.7's pattern: leave
    the design as written, add what the code does.
  • A test that the provisioned ACLs match the registry, or an explicit note that none
    exists.
  • Decide A vs B before EB_TENANCY_MODE=multi is recommended for anything real.

Scope note

Phase 3, not Phase 4, and filed separately from #897 for the same reason as the sibling
issue: the finding belongs with the code it describes. #897 depends on this — its §3
enrollment hands a user a topic whose access control is exactly what this issue is about,
and its §3.5 already records the narrower bound ("enrollment proves the GitHub account
asked for a topic [...] not that this topic reaches only that person's phone").

Verification

Statements about what the code does were checked against eventing/ at dc1d432:
NtfyPublisher publishes to self._cfg.topic with no per-user routing; no ACL or
subscription-authorization logic exists in eventbridge/ or shared/; registry.User
carries no permission field. Statements about ntfy's behaviour and the provisioning
caveats are quoted from DESIGN_PHASE3.md §4.3, which records them as design, and were
not re-verified against a running ntfy — no cluster was involved.

主要言語
Python
スター
11
フォーク
42
平均マージ
4日 11時間
マージ済み PR(30日)
40

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

rossoctl/examples のほかの issue

rossoctl/examples の issue をすべて見る

似ている issue

Python の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。