tinyrelayfile
tinyrelay/internal/records/sign.go/

internal/records/sign.go raw
1package records
2
3import (
4 "context"
5 "crypto/sha256"
6 "encoding/hex"
7 "fmt"
8 "github.com/FelineStateMachine/tinyrelay/internal/event"
9 "net/url"
10 "strings"
11 "time"
12)
13
14// GenerateEphemeral signs a relay-owned response without invoking the
15// durable records callback. It is used for protocol responses such as the
16// NIP-43 invite request, which must be relay-signed but never stored.
17func (s *Service) GenerateEphemeral(ctx context.Context, kind int, tags [][]string, content string, now int64) (event.Event, error) {
18 if err := ctx.Err(); err != nil {
19 return event.Event{}, err
20 }
21 if now == 0 {
22 now = time.Now().Unix()
23 }
24 // Protocol proofs use wall-clock time. The immutable relay key is safe to
25 // read concurrently; taking the projection lock here would invert the
26 // router/records lock order for responses generated inside a REQ.
27 out := event.Event{CreatedAt: now, Kind: kind, Tags: tags, Content: content}
28 if err := event.Sign(&out, s.secret); err != nil {
29 return event.Event{}, err
30 }
31 return out, nil
32}
33
34// SignNIP98 creates a short-lived HTTP authorization proof with the durable
35// relay identity. The caller supplies the exact URL Git will request and the
36// body hash when the method has a payload.
37func (s *Service) SignNIP98(ctx context.Context, method, rawURL, payloadHash string, now int64) (event.Event, error) {
38 if _, err := url.ParseRequestURI(rawURL); err != nil || !strings.Contains(rawURL, "://") {
39 return event.Event{}, fmt.Errorf("records: invalid NIP-98 URL: %w", err)
40 }
41 secret, err := event.GenerateKey()
42 if err != nil {
43 return event.Event{}, err
44 }
45 tags := [][]string{{"u", rawURL}, {"method", strings.ToUpper(method)}, {"nonce", secret}}
46 if payloadHash != "" {
47 if len(payloadHash) != sha256.Size*2 {
48 return event.Event{}, fmt.Errorf("records: invalid NIP-98 payload hash")
49 }
50 if _, err := hex.DecodeString(payloadHash); err != nil {
51 return event.Event{}, fmt.Errorf("records: invalid NIP-98 payload hash: %w", err)
52 }
53 tags = append(tags, []string{"payload", payloadHash})
54 }
55 if now == 0 {
56 now = time.Now().Unix()
57 }
58 return s.GenerateEphemeral(ctx, 27235, tags, "", now)
59}
60
61// SignNIP42 answers a relay challenge without persisting an AUTH event.
62func (s *Service) SignNIP42(ctx context.Context, relayURL, challenge string, now int64) (event.Event, error) {
63 if relayURL == "" || challenge == "" {
64 return event.Event{}, fmt.Errorf("records: missing NIP-42 challenge")
65 }
66 return s.GenerateEphemeral(ctx, event.KIND_AUTH, [][]string{{"relay", relayURL}, {"challenge", challenge}}, "", now)
67}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

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

Refs