diff --git a/README.md b/README.md
index 64301ca46..d3373eb91 100644
--- a/README.md
+++ b/README.md
@@ -85,7 +85,7 @@ The following config options are supported:
| timestampCertChain | | Path to PEM encoded certificate chain for RFC3161 Timestamp Authority verification. |
| autoclose | true | If true, autoclose the browser window after `autocloseTimeout`. In order for autoclose to work you must also set `connectorID`. |
| autocloseTimeout | 6 | If `autoclose` is true, this is how long to wait until the window is closed. |
-| enableSigstoreGo | false | (Experimental) If true, use the sigstore-go libraries for signing and verification via the CMS↔bundle compatibility layer. Requires `rekorMode=offline`. The on-disk signature format is unchanged. See [docs/bundle-cms.md](./docs/bundle-cms.md) for more details. |
+| enableSigstoreGo | true | If true (the default), use the sigstore-go libraries via the CMS↔bundle compatibility layer. Verification uses sigstore-go in all Rekor modes; signing uses it only in `rekorMode=offline` (online/legacy signing stays on the existing CMS path). Set to false to fall back to the legacy implementation. The on-disk signature format is unchanged. See [docs/bundle-cms.md](./docs/bundle-cms.md) for more details. |
| urlOpener | | Optional command used to open the OIDC login URL during the interactive auth flow. If unset, the platform default browser is used. The command is split using shell-style word splitting (quote arguments containing spaces) and each token is rendered as a Go [text/template](https://pkg.go.dev/text/template) with the login URL available as `{{.URL}}` (e.g. `firefox --new-tab {{.URL}}`). No shell is invoked, so shell metacharacters are inert. See [docs/url-opener.md](./docs/url-opener.md) for examples, including opening a specific Chrome profile. |
### Environment Variables
@@ -105,10 +105,10 @@ The following config options are supported:
| GITSIGN_TIMESTAMP_SERVER_URL | ✅ | | Address of timestamping authority. If set, a trusted timestamp will be included in the signature. |
| GITSIGN_TIMESTAMP_CERT_CHAIN | ✅ | | Path to PEM encoded certificate chain for RFC3161 Timestamp Authority verification. |
| GITSIGN_FULCIO_ROOT | ✅ | | Path to PEM encoded certificate for Fulcio CA (additional alias: SIGSTORE_ROOT_FILE) |
-| GITSIGN_REKOR_MODE | ❌ | online | Rekor storage mode to operate in. One of [online, offline] (default: online)
online - Commit SHAs are stored in Rekor, requiring online verification for all commit objects.
offline - Hashed commit content is stored in Rekor, with Rekor attributes necessary for offline verification being stored in the commit itself.
Note: online verification will be deprecated in favor of offline in the future. |
+| GITSIGN_REKOR_MODE | ❌ | offline | Rekor storage mode to operate in. One of [online, offline] (default: offline)
online - Commit SHAs are stored in Rekor, requiring online verification for all commit objects.
offline - Hashed commit content is stored in Rekor, with Rekor attributes necessary for offline verification being stored in the commit itself.
Note: online verification will be deprecated in favor of offline in the future. |
| GITSIGN_AUTOCLOSE | ❌ | true | If true, autoclose the browser window after `GITSIGN_AUTOCLOSE_TIME`. |
| GITSIGN_AUTOCLOSE_TIMEOUT | ❌ | 6 | If `GITSIGN_AUTOCLOSE` is true, this is how long to wait until the window is closed. |
-| GITSIGN_ENABLE_SIGSTORE_GO | ❌ | false | (Experimental) If true, use the sigstore-go libraries for signing and verification via the CMS↔bundle compatibility layer. Requires `GITSIGN_REKOR_MODE=offline`. The on-disk signature format is unchanged. See [docs/bundle-cms.md](./docs/bundle-cms.md) for more details. |
+| GITSIGN_ENABLE_SIGSTORE_GO | ❌ | true | If true (the default), use the sigstore-go libraries via the CMS↔bundle compatibility layer. Verification uses sigstore-go in all Rekor modes; signing uses it only in `GITSIGN_REKOR_MODE=offline` (online/legacy signing stays on the existing CMS path). Set to false to fall back to the legacy implementation. The on-disk signature format is unchanged. See [docs/bundle-cms.md](./docs/bundle-cms.md) for more details. |
| GITSIGN_URL_OPENER | ❌ | | Optional command used to open the OIDC login URL during the interactive auth flow. If unset, the platform default browser is used. The command is split using shell-style word splitting (quote arguments containing spaces) and each token is rendered as a Go text/template with the login URL available as `{{.URL}}` (e.g. `firefox --new-tab {{.URL}}`). No shell is invoked, so shell metacharacters are inert. See [docs/url-opener.md](./docs/url-opener.md) for examples, including opening a specific Chrome profile. |
For environment variables that support `Sigstore Prefix`, the values may be
@@ -301,7 +301,7 @@ Gitsign stores data in 2 places:
time, Gitsign records commits and the code signing certificates to
[Rekor](https://docs.sigstore.dev/rekor/overview/).
- - If `rekorMode = online` (default)
+ - If `rekorMode = online`
This data is a
[HashedRekord](https://github.com/sigstore/rekor/blob/e375eb461cae524270889b57a249ff086bea6c05/types.md#hashed-rekord)
@@ -310,10 +310,7 @@ Gitsign stores data in 2 places:
[Verifying the Transparency Log](#verifying-the-transparency-log) for more
details.
- - If `rekorMode = offline`
-
- Note: offline verification is new, and should be considered experimental for
- now.
+ - If `rekorMode = offline` (default)
By default, data is written to the
[public Rekor instance](https://docs.sigstore.dev/rekor/public-instance). In
diff --git a/docs/bundle-cms.md b/docs/bundle-cms.md
index 226207f11..373f4d097 100644
--- a/docs/bundle-cms.md
+++ b/docs/bundle-cms.md
@@ -14,8 +14,9 @@ explains how the two formats relate and how the conversion works. The
compatibility layer lives in
[`internal/sigstore/compat`](../internal/sigstore/compat).
-> **Note:** This conversion is currently experimental and opt-in. See
-> [Enabling the sigstore-go path](#enabling-the-sigstore-go-path) below.
+> **Note:** This conversion is enabled by default. See
+> [Enabling the sigstore-go path](#enabling-the-sigstore-go-path) below for how
+> to disable it or how it interacts with the Rekor mode.
## The key insight: the signed artifact is the SignedAttrs
@@ -104,17 +105,23 @@ Because the signature is over the `SignedAttrs` — which include the signing ti
## Enabling the sigstore-go path
-Both directions are gated behind a single experimental option, off by default:
+The sigstore-go path is controlled by a single option, on by default:
```sh
-git config gitsign.enableSigstoreGo true
-# or: GITSIGN_ENABLE_SIGSTORE_GO=true
+# Disable it and fall back to the legacy CMS + Rekor implementation:
+git config gitsign.enableSigstoreGo false
+# or: GITSIGN_ENABLE_SIGSTORE_GO=false
```
-This requires offline Rekor mode (`gitsign.rekorMode=offline`); gitsign will
-error at startup otherwise, since the bundle signing path embeds the Rekor entry
-in the signature (which is meaningless in online mode). Online/legacy signing
-remains on the existing CMS path.
+The two directions engage differently depending on the Rekor mode:
+
+- **Verification** uses sigstore-go in all Rekor modes. Legacy online signatures
+ carry no embedded Rekor entry — sigstore-go cannot look these up from the
+ signature alone, so they transparently fall back to the legacy verifier.
+- **Signing** uses sigstore-go only in offline Rekor mode
+ (`gitsign.rekorMode=offline`), where the bundle signing path embeds the Rekor
+ entry in the signature. Online/legacy signing remains on the existing CMS path
+ even when the option is enabled.
The on-disk CMS signature format is identical whether or not the option is set;
only the implementation of how signing and verification are performed changes.
diff --git a/internal/commands/root/sign.go b/internal/commands/root/sign.go
index 7efea1295..77614da0b 100644
--- a/internal/commands/root/sign.go
+++ b/internal/commands/root/sign.go
@@ -42,8 +42,11 @@ func commandSign(o *options, s *gsio.Streams, args ...string) error {
return errors.New("specify --help, --sign, or --verify")
}
- if o.Config.EnableSigstoreGo {
- fmt.Fprintln(s.TTYOut, "gitsign: experimental sigstore-go signing enabled") // nolint:errcheck
+ // The sigstore-go signing path only engages in offline Rekor mode (it embeds
+ // the Rekor entry in the signature); online/legacy signing stays on the CMS
+ // path even when sigstore-go is enabled.
+ if o.Config.EnableSigstoreGo && o.Config.RekorMode == "offline" {
+ fmt.Fprintln(s.TTYOut, "gitsign: sigstore-go signing enabled") // nolint:errcheck
}
userIdent, err := fulcio.NewIdentity(ctx, o.Config, s.TTYIn, s.TTYOut)
diff --git a/internal/config/config.go b/internal/config/config.go
index 1f3021f81..45b6f283a 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -49,17 +49,22 @@ type Config struct {
// Address of Rekor server
Rekor string
- // Rekor storage mode to operate in. One of [online, offline] (default: online)
+ // Rekor storage mode to operate in. One of [online, offline] (default: offline)
// online - Commit SHAs are stored in Rekor, requiring online verification for all commit objects.
// offline - Hashed commit content is stored in Rekor, with Rekor attributes
// necessary for offline verification being stored in the commit itself.
// Note: online verification will be deprecated in favor of offline in the future.
RekorMode string
- // EnableSigstoreGo enables the experimental sigstore-go code paths for both
- // signing and verification (via the CMS<->bundle compat layer). When false
- // (the default), the legacy CMS + Rekor signing and verification are used.
- // The on-disk CMS signature format is unchanged either way.
+ // EnableSigstoreGo enables the sigstore-go code paths (via the CMS<->bundle
+ // compat layer). It is on by default; set it to false to fall back to the
+ // legacy CMS + Rekor implementation. The on-disk CMS signature format is
+ // unchanged either way.
+ //
+ // Verification uses sigstore-go in all Rekor modes (legacy online signatures
+ // with no embedded entry fall back to the legacy verifier). Signing uses
+ // sigstore-go only in offline Rekor mode, where the Rekor entry is embedded
+ // in the signature; online/legacy signing stays on the existing CMS path.
EnableSigstoreGo bool
// OIDC client ID for application
@@ -147,12 +152,12 @@ func Get() (*Config, error) {
// Start with default config
out := &Config{
- Fulcio: "https://fulcio.sigstore.dev",
- Rekor: "https://rekor.sigstore.dev",
- ClientID: "sigstore",
- Issuer: "https://oauth2.sigstore.dev/auth",
- // TODO: default to offline
- RekorMode: "online",
+ Fulcio: "https://fulcio.sigstore.dev",
+ Rekor: "https://rekor.sigstore.dev",
+ ClientID: "sigstore",
+ Issuer: "https://oauth2.sigstore.dev/auth",
+ RekorMode: "offline",
+ EnableSigstoreGo: true,
Autoclose: true,
AutocloseTimeout: 6,
}
@@ -190,13 +195,6 @@ func Get() (*Config, error) {
out.URLOpener = envOrValue("GITSIGN_URL_OPENER", out.URLOpener)
out.EnableSigstoreGo = envOrValue("GITSIGN_ENABLE_SIGSTORE_GO", fmt.Sprintf("%t", out.EnableSigstoreGo)) == "true"
- // The sigstore-go signing path embeds the Rekor entry in the signature, which
- // only applies to offline Rekor mode. Fail loudly rather than silently
- // ignoring the setting in online mode.
- if out.EnableSigstoreGo && out.RekorMode != "offline" {
- return nil, fmt.Errorf("gitsign.enableSigstoreGo requires gitsign.rekorMode=offline, got %q", out.RekorMode)
- }
-
return out, nil
}
diff --git a/internal/config/config_test.go b/internal/config/config_test.go
index d7c0a6163..5129fa314 100644
--- a/internal/config/config_test.go
+++ b/internal/config/config_test.go
@@ -86,7 +86,8 @@ func TestGet(t *testing.T) {
Issuer: "tacocat",
RedirectURL: "example.com",
ConnectorID: "bar",
- RekorMode: "online",
+ RekorMode: "offline",
+ EnableSigstoreGo: true,
Autoclose: true,
AutocloseTimeout: 6,
// From config file.
@@ -107,22 +108,44 @@ func TestGet(t *testing.T) {
}
}
-func TestEnableSigstoreGoRequiresOffline(t *testing.T) {
+func TestEnableSigstoreGo(t *testing.T) {
t.Cleanup(func() { execFn = realExec })
- // enableSigstoreGo with the default (online) Rekor mode must error.
+ // sigstore-go is opted in by default, including in the default (online)
+ // Rekor mode.
execFn = func() (io.Reader, error) {
- return strings.NewReader("gitsign.enableSigstoreGo true\n"), nil
+ return strings.NewReader(""), nil
}
- if _, err := Get(); err == nil {
- t.Error("expected error when enableSigstoreGo is set without offline rekor mode")
+ got, err := Get()
+ if err != nil {
+ t.Fatalf("unexpected error with default config: %v", err)
+ }
+ if !got.EnableSigstoreGo {
+ t.Error("expected EnableSigstoreGo to default to true")
}
- // enableSigstoreGo with offline mode is allowed.
+ // It can be turned off via git config.
execFn = func() (io.Reader, error) {
- return strings.NewReader("gitsign.enableSigstoreGo true\ngitsign.rekorMode offline\n"), nil
+ return strings.NewReader("gitsign.enableSigstoreGo false\n"), nil
+ }
+ got, err = Get()
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ if got.EnableSigstoreGo {
+ t.Error("expected gitsign.enableSigstoreGo=false to disable sigstore-go")
+ }
+
+ // It can be turned off via environment variable.
+ t.Setenv("GITSIGN_ENABLE_SIGSTORE_GO", "false")
+ execFn = func() (io.Reader, error) {
+ return strings.NewReader(""), nil
+ }
+ got, err = Get()
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
}
- if _, err := Get(); err != nil {
- t.Errorf("expected enableSigstoreGo + offline mode to be valid, got: %v", err)
+ if got.EnableSigstoreGo {
+ t.Error("expected GITSIGN_ENABLE_SIGSTORE_GO=false to disable sigstore-go")
}
}