feat(14.3-01): export lagoon.GenerateAppKey

- key:generate now calls GenerateAppKey; its output is unchanged
- tools can write a fresh app.key without the check-mark decoration
- document lagoon.GenerateAppKey in the lagoon README

Claude-Session: https://claude.ai/code/session_01M8Qu9LgCKYy7iYEho52tW8
This commit is contained in:
Jakub Zych
2026-10-08 11:41:38 +02:00
parent cc7386d22e
commit ca5b2963a5
3 changed files with 33 additions and 3 deletions

View File

@@ -148,6 +148,7 @@ func (p *Plugin) Migrations() []*gormigrate.Migration {
| `lagoon.FrameworkSchedule` | The framework's scheduled commands for conga: the daily `deferred:purge` at `database.deferred_bindings.purge_at`, or none when it is empty. |
| `lagoon.FrameworkScheduleID` | Plugin id the framework's schedule entries are listed under, `summercms.lagoon`. |
| `lagoon.KeyGenerateCommand` | Returns the key:generate command on its own. |
| `lagoon.GenerateAppKey` | Returns a fresh 32-byte base64 `app.key`, the value key:generate prints, for tools that write it into a file. |
| `lagoon.LoadAppKey` | Decodes `app.key` and `app.previous_keys`. |
| `lagoon.PublishEncryptionKeys` | Installs the keys used by `lagoon.Encrypted` columns. |
| `lagoon.Encrypted` | Encrypted text column; `lagoon.Encrypted.Reveal` is the only plaintext accessor. |

View File

@@ -8,6 +8,17 @@ import (
"git.golem15.com/golem15/summercms/modules/bonfire"
)
// GenerateAppKey returns a fresh app.key: 32 random bytes from crypto/rand,
// base64 (standard encoding) encoded, 44 characters. It is the value
// key:generate prints, without the output decoration.
func GenerateAppKey() (string, error) {
key := make([]byte, encryptedKeySize)
if _, err := rand.Read(key); err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(key), nil
}
// KeyGenerateCommand prints a fresh 32-byte base64 app.key and performs no
// other side effect (D-11).
func KeyGenerateCommand() bonfire.Command {
@@ -15,11 +26,11 @@ func KeyGenerateCommand() bonfire.Command {
Name: "key:generate",
Description: "Print a fresh 32-byte base64 app.key",
Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error {
key := make([]byte, encryptedKeySize)
if _, err := rand.Read(key); err != nil {
key, err := GenerateAppKey()
if err != nil {
return err
}
out.Success(base64.StdEncoding.EncodeToString(key))
out.Success(key)
return nil
},
}

View File

@@ -38,3 +38,21 @@ func TestEncryptedKeyGeneratePrints32ByteBase64(t *testing.T) {
t.Fatalf("decoded key length = %d, want 32", len(raw))
}
}
func TestGenerateAppKeyIs32ByteBase64(t *testing.T) {
a, err := GenerateAppKey()
if err != nil {
t.Fatal(err)
}
b, err := GenerateAppKey()
if err != nil {
t.Fatal(err)
}
if len(a) != 44 || a == b {
t.Fatalf("keys %q and %q: want two distinct 44-character values", a, b)
}
raw, err := base64.StdEncoding.DecodeString(a)
if err != nil || len(raw) != 32 {
t.Fatalf("key %q decodes to %d bytes, err %v", a, len(raw), err)
}
}