From a8871da1004d9d9c1a9fe533de543bafa73c9c3d Mon Sep 17 00:00:00 2001 From: Jon Staab Date: Mon, 22 Jun 2026 17:22:15 -0700 Subject: [PATCH] Add signevent method --- go.mod | 2 +- go.sum | 4 +-- zooid/management.go | 62 +++++++++++++++++++++++++++++++--------- zooid/management_test.go | 41 ++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 17 deletions(-) diff --git a/go.mod b/go.mod index 3ead436..57b1520 100644 --- a/go.mod +++ b/go.mod @@ -131,4 +131,4 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect ) -replace fiatjaf.com/nostr => gitea.coracle.social/Coracle/nostrlib v0.0.0-20260622165044-a525b660543d +replace fiatjaf.com/nostr => gitea.coracle.social/Coracle/nostrlib v0.0.0-20260623001341-fa7d25a59b3d diff --git a/go.sum b/go.sum index 30d3bd8..c69d444 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,8 @@ dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= fiatjaf.com/lib v0.3.7 h1:mXZOn7NrUcjSdy4oNvwQyAmes7Ueb+Zr5hjqMIe2dxI= fiatjaf.com/lib v0.3.7/go.mod h1:UlHaZvPHj25PtKLh9GjZkUHRmQ2xZ8Jkoa4VRaLeeQ8= -gitea.coracle.social/Coracle/nostrlib v0.0.0-20260622165044-a525b660543d h1:WAtkiBuX8fZSSjerUxueHCFwuWGacEFWkm17fiTb0iU= -gitea.coracle.social/Coracle/nostrlib v0.0.0-20260622165044-a525b660543d/go.mod h1:b1EIUDnd133Ie8Pg8O/biaKdFyCMz28aD4n64g1GqvM= +gitea.coracle.social/Coracle/nostrlib v0.0.0-20260623001341-fa7d25a59b3d h1:Ws4dqvn6Fou5DvrdrrctHfbXgENPpa+QBXXRb95P3Zw= +gitea.coracle.social/Coracle/nostrlib v0.0.0-20260623001341-fa7d25a59b3d/go.mod h1:b1EIUDnd133Ie8Pg8O/biaKdFyCMz28aD4n64g1GqvM= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= diff --git a/zooid/management.go b/zooid/management.go index 76b7433..0c5acc5 100644 --- a/zooid/management.go +++ b/zooid/management.go @@ -321,21 +321,21 @@ func (m *ManagementStore) DeleteRole(id string) error { return err } - address := nostr.EntityPointer{ - Kind: event.Kind, - PublicKey: event.PubKey, - Identifier: event.Tags.GetD(), - }.AsTagReference() + address := nostr.EntityPointer{ + Kind: event.Kind, + PublicKey: event.PubKey, + Identifier: event.Tags.GetD(), + }.AsTagReference() - event := nostr.Event{ - Kind: nostr.KindDeletion, - CreatedAt: nostr.Now(), - Tags: nostr.Tags{ - nostr.Tag{"e", event.ID.Hex()}, - nostr.Tag{"a", address}, - nostr.Tag{"k", strconv.Itoa(int(event.Kind))}, - }, - } + event := nostr.Event{ + Kind: nostr.KindDeletion, + CreatedAt: nostr.Now(), + Tags: nostr.Tags{ + nostr.Tag{"e", event.ID.Hex()}, + nostr.Tag{"a", address}, + nostr.Tag{"k", strconv.Itoa(int(event.Kind))}, + }, + } if err := m.Events.SignAndStoreEvent(&event, true); err != nil { return err @@ -435,6 +435,36 @@ func (m *ManagementStore) removeRoleFromMembers(roleID string) error { return m.Events.SignAndStoreEvent(&membersEvent, true) } +// Signing + +// SignEvent signs an event template with the relay's identity key on an admin's behalf, then +// stores and broadcasts it before returning the signed event. Only kind 30078 (application-specific +// data) is supported for now; every other kind is rejected outright. +func (m *ManagementStore) SignEvent(kind nostr.Kind, createdAt nostr.Timestamp, tags nostr.Tags, content string) (nostr.Event, error) { + if kind != nostr.KindApplicationSpecificData { + return nostr.Event{}, errors.New("kind not allowed") + } + + // A missing created_at would otherwise default to the epoch, which is almost never what + // the caller intends, so fall back to the current time. + if createdAt == 0 { + createdAt = nostr.Now() + } + + event := nostr.Event{ + Kind: kind, + CreatedAt: createdAt, + Tags: tags, + Content: content, + } + + if err := m.Events.SignAndStoreEvent(&event, true); err != nil { + return nostr.Event{}, err + } + + return event, nil +} + // Banning func (m *ManagementStore) BanPubkey(pubkey nostr.PubKey, reason string) error { @@ -610,4 +640,8 @@ func (m *ManagementStore) Enable(instance *Instance) { instance.Relay.ManagementAPI.UnassignRole = func(ctx context.Context, pubkey nostr.PubKey, roleID string) error { return m.UnassignRole(pubkey, roleID) } + + instance.Relay.ManagementAPI.SignEvent = func(ctx context.Context, kind nostr.Kind, createdAt nostr.Timestamp, tags nostr.Tags, content string) (nostr.Event, error) { + return m.SignEvent(kind, createdAt, tags, content) + } } diff --git a/zooid/management_test.go b/zooid/management_test.go index 5da3c61..2a4309c 100644 --- a/zooid/management_test.go +++ b/zooid/management_test.go @@ -438,6 +438,47 @@ func TestManagementStore_DeleteRole_BroadcastsDeletion(t *testing.T) { } } +func TestManagementStore_SignEvent_AllowedKind(t *testing.T) { + mgmt := createTestManagementStore() + + tags := nostr.Tags{nostr.Tag{"d", "zooid/test"}} + + event, err := mgmt.SignEvent(nostr.KindApplicationSpecificData, 0, tags, "hello") + if err != nil { + t.Fatalf("SignEvent() error = %v", err) + } + + if event.PubKey != mgmt.Config.GetSelf() { + t.Errorf("SignEvent() signed with %s, want relay key %s", event.PubKey, mgmt.Config.GetSelf()) + } + + if !event.VerifySignature() { + t.Error("SignEvent() produced an invalid signature") + } + + // A zero created_at must be replaced with the current time. + if event.CreatedAt == 0 { + t.Error("SignEvent() should default a missing created_at to the current time") + } + + // SignEvent must not persist the event, only return it. + filter := nostr.Filter{Kinds: []nostr.Kind{nostr.KindApplicationSpecificData}, Tags: nostr.TagMap{"d": []string{"zooid/test"}}} + + for stored := range mgmt.Events.QueryEvents(filter, 1) { + if stored.ID == event.ID { + t.Error("SignEvent() should not store the signed event") + } + } +} + +func TestManagementStore_SignEvent_RejectsOtherKinds(t *testing.T) { + mgmt := createTestManagementStore() + + if _, err := mgmt.SignEvent(nostr.KindTextNote, 0, nil, ""); err == nil || err.Error() != "kind not allowed" { + t.Errorf("SignEvent() error = %v, want \"kind not allowed\"", err) + } +} + func TestManagementStore_PubkeyIsBanned_NotBanned(t *testing.T) { mgmt := createTestManagementStore()