Skip to content

feat(entities): record unauthorized interactions + operator list endpoint#118

Merged
AquiGorka merged 2 commits into
mainfrom
feat/entities-record-interactions-list-endpoint
Jun 12, 2026
Merged

feat(entities): record unauthorized interactions + operator list endpoint#118
AquiGorka merged 2 commits into
mainfrom
feat/entities-record-interactions-list-endpoint

Conversation

@AquiGorka

@AquiGorka AquiGorka commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Summary

A PP operator had no way to see which entities have interacted with their provider. Entities that registered via KYC self-serve get an APPROVED row in pp_entity_approvals; entities that interact but are NOT authorized (SEP-10 connect with no approval, or a bundle-submit 403) left zero persistent trace. This persists a record at those two unauthorized-interaction points and exposes an operator-facing list endpoint.

Vertical slice with the UI side in Moonlight-Protocol/provider-console#38.

What changed

  • recordInteraction(pp, pubkey) — single ON CONFLICT … DO UPDATE … setWhere status = 'UNVERIFIED' upsert: insert UNVERIFIED if absent, touch updated_at if the row is already UNVERIFIED, and never modify an APPROVED/PENDING/BLOCKED row.
  • listByPp(pp) — non-deleted rows for the PP joined to entity identity (name/jurisdictions) via account → entity, updated_at DESC.
  • Write-points — SEP-10 connect (attach-entity-status.ts) and the bundle-submit 403 gate (add-bundle.process.ts, both branches). Both are best-effort: a write failure never breaks the auth response and never changes the 403.
  • GET /api/v1/providers/:ppPublicKey/entities — operator JWT + requirePpOwnership, mirroring the metrics endpoint ({ data } envelope).
  • Reuses the existing table + entity_status enum — no migration.

Testing

  • Unit (PGlite, real migrations): the upsert invariant across all branches (insert / touch-when-UNVERIFIED / no-op-when-APPROVED/PENDING/BLOCKED), plus listByPp ordering + join + soft-delete/PP scoping.
  • Integration: endpoint envelope + updated_at DESC + ownership (owner passes, non-owner denied).
  • Live local smoke (isolated Docker stack built from this branch): approved entity submits a real bundle; unauthorized connect records UNVERIFIED; unauthorized bundle 403s and advances updated_at on the existing row; operator endpoint returns all three in DESC; non-owner denied. No authorization regression.
  • deno fmt --check, deno lint, deno task test (200 passed) all green.

Intentional deviation from the prompt text

A non-owning operator receives 404 ("Provider not found"), not 403 — requirePpOwnership returns 404 for any PP the operator doesn't own, identical to every other /providers/:pp endpoint (it doesn't distinguish "exists-but-not-yours" from "absent"). The ownership property holds; only the status code differs. Tests assert the real 404.

Relates to ClickUp 86ca439ah

…oint

Persist a pp_entity_approvals row at the two unauthorized-interaction points
(SEP-10 connect + bundle-submit 403) so unauthorized pubkeys become visible to
operators, and expose them via GET /providers/:ppPublicKey/entities.

- recordInteraction: insert UNVERIFIED if absent, touch updated_at if already
  UNVERIFIED, never modify an APPROVED/PENDING/BLOCKED row (single ON CONFLICT
  upsert with setWhere status = UNVERIFIED).
- listByPp: non-deleted rows joined to entity identity, updated_at DESC.
- Wire both write-points best-effort (a write failure never breaks the auth
  response or changes the 403).
- Operator list endpoint mirrors the metrics endpoint (jwt + requirePpOwnership,
  { data } envelope).
- Reuses the existing table + enum; no migration.

Relates to ClickUp 86ca439ah
@AquiGorka
AquiGorka force-pushed the feat/entities-record-interactions-list-endpoint branch from e001f4c to df30e56 Compare June 11, 2026 22:03
AquiGorka added a commit to Moonlight-Protocol/provider-console that referenced this pull request Jun 12, 2026
…38)

## Summary

Adds an "Entities" section to the provider detail page showing every
pubkey that has interacted with the provider — the KYC-approved ones
plus the unauthorized pubkeys now recorded by
Moonlight-Protocol/provider-platform#118 (SEP-10 connect + bundle-submit
403).

UI side of the vertical slice; backend is
Moonlight-Protocol/provider-platform#118.

## What changed

- New `getEntities(ppPublicKey)` client call (`lib/api.ts`) following
the existing URL-scoped pattern (`{ data }` envelope).
- New **Entities** section in `views/provider.ts`, **below** all
existing sections (OpEx / Councils / Dashboard / sparklines). Columns:
Date (`updated_at`, relative — matching the console's existing date
helper), Public Key (truncated + copy affordance), Status (badge per
status). Loading / empty ("No entities have interacted with this
provider yet") / error states match the sibling sections.
- `.badge-unverified` neutral grey variant in `app-styles.css` (the lib
ships active/pending/inactive; UNVERIFIED has no semantic colour).

## Testing

- `deno fmt --check`, `deno task lint`, `deno task test`, `deno task
build` all green.
- Live local smoke (isolated Docker stack built from this branch +
provider-platform#118): the section renders three real rows — two
UNVERIFIED (from the connect + bundle-403 write-points) and one APPROVED
(KYC) — in `updated_at` DESC, below the existing sections. Screenshot
captured.

## Note

The `Date` column uses the console's existing relative-time helper
(`fmtRelativeTime`) — the only date format the console currently ships —
to match convention. Happy to switch to an absolute timestamp if
preferred.

Relates to ClickUp 86ca439ah
@AquiGorka
AquiGorka merged commit 4d08385 into main Jun 12, 2026
7 checks passed
@AquiGorka
AquiGorka deleted the feat/entities-record-interactions-list-endpoint branch June 12, 2026 01:08
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.

1 participant