Files
MealQuest/cmd/vapidgen/main.go
T
dev_agentandClaude Opus 5.5 f1e7503f94
CI / ci (push) Successful in 6m3s
CI / ci (pull_request) Successful in 5m17s
Add VAPID config and the push subscription API
Require MEALQUEST_VAPID_PUBLIC_KEY, MEALQUEST_VAPID_PRIVATE_KEY_FILE and
MEALQUEST_VAPID_SUBJECT at boot. Both keys are unpadded base64url P-256
keys, the encoding webpush-go reads; boot derives the public point from the
private key and refuses a pair that does not match. The subject must be
mailto:<address> or an https:// URL.

internal/push stores subscriptions by endpoint: GET
/api/v1/push/vapid-public-key serves only the public key, POST
/api/v1/push/subscriptions upserts {endpoint, keys.p256dh, keys.auth,
user_agent}, and DELETE removes by endpoint, answering 204 whether or not
it was stored. cmd/vapidgen prints a new key pair. deploy/compose.yaml
mounts the vapid_private_key secret.

Maestro task e8a7b1f4-327a-478b-88c7-5d265afda153.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 08:03:59 -04:00

40 lines
1.2 KiB
Go

// Command vapidgen prints a new VAPID key pair for Web Push. The public key
// line goes into deploy/.env as is; the private key line goes, alone, into
// the secret file MEALQUEST_VAPID_PRIVATE_KEY_FILE names. Both keys are
// unpadded base64url P-256 keys, the encoding config.Load validates.
//
// go run ./cmd/vapidgen
package main
import (
"fmt"
"io"
"os"
"code.cubecraftlabs.com/Maniacs_Mansion/Meal-Quest/internal/config"
"code.cubecraftlabs.com/Maniacs_Mansion/Meal-Quest/internal/vapid"
)
func main() {
if len(os.Args) > 1 {
fmt.Fprintln(os.Stderr, "usage: vapidgen (takes no arguments)")
os.Exit(2)
}
if err := run(os.Stdout); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
// run writes a new key pair to w in four lines: a comment and the public
// key's environment line, then a comment and the private key alone.
func run(w io.Writer) error {
pair, err := vapid.Generate()
if err != nil {
return err
}
_, err = fmt.Fprintf(w, "# Environment (deploy/.env):\n%s=%s\n# Private key: this next line alone is the file %s names.\n%s\n",
config.EnvVAPIDPublicKey, pair.PublicKey, config.EnvVAPIDPrivateKeyFile, pair.PrivateKey)
return err
}