clone

a self-hosted multitenant nostr relay with sites, file storage, git hosting, and inbox and outbox delivery

fix: keep grant decisions consistent during concurrent changes

Dami committed 688625f
Match approval denial text consistently, respect reaction expiry, and timestamp replacement grants after their base even within the same second.

Verify denied requests cannot apply and prepared replacements advance addressable event ordering.
commit
688625fe875d99addb80ff7582633e56ea6f93b5
parent
7d475627c98871df2ec1345e4c91c911b6f4702e
author
Dami <dami.etoile@gmail.com>
2 files changed, +36 -8
Minternal/community/agent_grant_requests.go24+++++++++++++++++-------
Minternal/daemon/grant_approvals_test.go20+++++++++++++++++++-
M internal/community/agent_grant_requests.go +17 -7
@@ -23,23 +23,30 @@ const agentGrantReasonMax = 500
2323 2424 type grantRequestReader interface {2525 QueryRowContext(context.Context, string, ...any) *sql.Row26+ QueryContext(context.Context, string, ...any) (*sql.Rows, error)2627 }2728 2829 func grantRequestOpen(ctx context.Context, db grantRequestReader, request event.Event, now int64) error {2930 if expires := event.Expiration(request); expires != 0 && expires <= now {3031 return errors.New("conflict: grant request has expired")3132 }32- var denied bool33- err := db.QueryRowContext(ctx, `SELECT EXISTS(SELECT 1 FROM events AS reaction34- WHERE kind=7 AND pubkey=? AND trim(json_extract(raw,'$.content'))='-' AND EXISTS(35- SELECT 1 FROM tags WHERE event_id=reaction.id AND name='e' AND value=?))`, event.Tag(request, "p"), request.ID).Scan(&denied)33+ rows, err := db.QueryContext(ctx, `SELECT json_extract(raw,'$.content') FROM events AS reaction34+ WHERE kind=7 AND pubkey=? AND (expires=0 OR expires>?) AND EXISTS(35+ SELECT 1 FROM tags WHERE event_id=reaction.id AND name='e' AND value=?)`, event.Tag(request, "p"), now, request.ID)3636 if err != nil {3737 return err3838 }39- if denied {40- return errors.New("conflict: grant request was denied")39+ defer rows.Close()40+ for rows.Next() {41+ var content string42+ if err := rows.Scan(&content); err != nil {43+ return err44+ }45+ if strings.TrimSpace(content) == "-" {46+ return errors.New("conflict: grant request was denied")47+ }4148 }42- return nil49+ return rows.Err()4350 }4451 4552 type AgentGrantChanges struct {
@@ -193,6 +200,9 @@ func (s *Service) reviewAgainstGrant(e event.Event, req AgentGrantRequest, befor
193200 afterEvent.Tags = grantRequestTags(beforeEvent.Tags, req.Changes)194201 afterEvent.Tags = append(afterEvent.Tags, []string{"grant-request", e.ID}, []string{"grant-base", req.Base}, []string{"e", e.ID, "", "grant-request"})195202 afterEvent.ID, afterEvent.Sig, afterEvent.CreatedAt = "", "", now203+ if afterEvent.CreatedAt <= beforeEvent.CreatedAt {204+ afterEvent.CreatedAt = beforeEvent.CreatedAt + 1205+ }196206 after, err := ParseAgentGrant(afterEvent, now)197207 if err != nil {198208 return AgentGrantReview{}, err
M internal/daemon/grant_approvals_test.go +19 -1