tinyrelayfile
tinyrelay/internal/daemon/grant_approvals.go/

internal/daemon/grant_approvals.go raw
1package daemon
2
3import (
4 "context"
5 "encoding/json"
6 "strings"
7
8 "github.com/FelineStateMachine/tinyrelay/internal/event"
9)
10
11// Keep the decision visible after the addressable grant has been superseded
12// or expired. Only archived grants tied to these exact requests are read.
13func (t *Tenant) grantApprovalHistory(ctx context.Context, items []approvalItem) ([]event.Event, error) {
14 ids, authors := []string{}, []string{}
15 for _, item := range items {
16 if item.Type == "grant" {
17 ids = append(ids, item.ID)
18 authors = append(authors, item.Asked...)
19 }
20 }
21 if len(ids) == 0 {
22 return nil, nil
23 }
24 idJSON, err := json.Marshal(ids)
25 if err != nil {
26 return nil, err
27 }
28 authorJSON, err := json.Marshal(uniqueStrings(authors))
29 if err != nil {
30 return nil, err
31 }
32 rows, err := t.store.DB().QueryContext(ctx, `SELECT raw FROM (
33 SELECT raw,created_at FROM events WHERE kind=30392 AND pubkey IN (SELECT value FROM json_each(?))
34 UNION ALL SELECT raw,created_at FROM agent_grant_revisions WHERE author IN (SELECT value FROM json_each(?))
35 ) WHERE EXISTS (SELECT 1 FROM json_each(json_extract(raw,'$.tags')) AS tag
36 WHERE json_extract(tag.value,'$[0]')='grant-request' AND json_extract(tag.value,'$[1]') IN (SELECT value FROM json_each(?)))
37 ORDER BY created_at DESC LIMIT ?`, string(authorJSON), string(authorJSON), string(idJSON), approvalScan)
38 if err != nil {
39 return nil, err
40 }
41 defer rows.Close()
42 var grants []event.Event
43 for rows.Next() {
44 var raw string
45 if err := rows.Scan(&raw); err != nil {
46 return nil, err
47 }
48 grant, err := event.Parse([]byte(raw))
49 if err != nil {
50 return nil, err
51 }
52 grants = append(grants, grant)
53 }
54 return grants, rows.Err()
55}
56
57// A grant is approved only by the operator's replacement grant. Reactions
58// cannot confer permissions or make an unapplied request look successful.
59func settleGrantApproval(item *approvalItem, rows []event.Event, now int64) {
60 for _, row := range rows {
61 if row.Kind != event.KIND_AGENT_GRANT || row.PubKey == item.Asker || !containsString(item.Asked, row.PubKey) || event.Tag(row, "grant-request") != item.ID || event.Tag(row, "grant-base") != event.Tag(item.Event, "E") || event.Tag(row, "d") != item.Asker {
62 continue
63 }
64 item.State = "answered"
65 item.Answer = &approvalAnswer{ID: row.ID, Kind: row.Kind, Author: row.PubKey, Decision: "approved", CreatedAt: row.CreatedAt}
66 return
67 }
68 for _, row := range rows {
69 if row.Kind != kindReaction || strings.TrimSpace(row.Content) != "-" || !containsString(item.Asked, row.PubKey) {
70 continue
71 }
72 item.State = "answered"
73 item.Answer = &approvalAnswer{ID: row.ID, Kind: row.Kind, Author: row.PubKey, Decision: "denied", CreatedAt: row.CreatedAt}
74 return
75 }
76 if item.Expires > 0 && item.Expires <= now {
77 item.State = "expired"
78 }
79}
80
81func (t *Tenant) reviewGrantApproval(ctx context.Context, item *approvalItem, now int64) {
82 if item.Type != "grant" || item.State != "open" {
83 return
84 }
85 review, err := t.community.ReviewAgentGrantRequest(ctx, item.Event, now)
86 if err != nil {
87 item.GrantError = err.Error()
88 if strings.HasPrefix(item.GrantError, "conflict:") {
89 item.State = "expired"
90 }
91 return
92 }
93 item.Grant = &review
94}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs