feat(proto): commit signature verification and trusted keys (#20)
- commit
- 4269eaf418f570f2486a5005717ff5dae200e427
- author
- brice · · 2026-10-18 18:03 UTC
- committer
- bnema · · 2026-10-18 18:03 UTC
- parent
- e06a09c
- signature
- SSH Unverified
SHA256:VOCA5lYARHcbKDZ7GhTV45VmcjWnJ6ZQIBazj17LivAThe key is unknown - refs
- tag: v0.6.0
7 files · +1099 -9
M README.md +1 -0
| @@ -41,6 +41,7 @@ Status: early development. | |||
| 41 | 41 | | `SecretService`, `VariableService`, `EnvironmentService` | CI settings | | |
| 42 | 42 | | `ArtifactService` | CI artifacts, for people | | |
| 43 | 43 | | `JobArtifactService` | CI artifacts v4, for runners | | |
| 44 | + | | `TrustedKeyService` | SSH and OpenPGP keys commit signatures are checked against | | |
| 44 | 45 | ||
| 45 | 46 | Every RPC: [docs/services.md](docs/services.md). | |
| 46 | 47 | ||
| @@ -41,6 +41,7 @@ Status: early development. | |||||
| 41 | | `SecretService`, `VariableService`, `EnvironmentService` | CI settings | | 41 | | `SecretService`, `VariableService`, `EnvironmentService` | CI settings | | ||
| 42 | | `ArtifactService` | CI artifacts, for people | | 42 | | `ArtifactService` | CI artifacts, for people | | ||
| 43 | | `JobArtifactService` | CI artifacts v4, for runners | | 43 | | `JobArtifactService` | CI artifacts v4, for runners | | ||
| 44 | + | | `TrustedKeyService` | SSH and OpenPGP keys commit signatures are checked against | | |||
| 44 | 45 | ||||
| 45 | Every RPC: [docs/services.md](docs/services.md). | 46 | Every RPC: [docs/services.md](docs/services.md). | ||
| 46 | 47 | ||||
M docs/services.md +1 -0
| @@ -9,6 +9,7 @@ | |||
| 9 | 9 | | `SessionService` | `CreateLoginLink`, `SignOutEverywhere` (owner only) | | |
| 10 | 10 | | `EventService` | `Watch` (streamed, resumable) | | |
| 11 | 11 | | `ServerService` | `GetVersion` (version and features), `GetSigningKey`, `RotateOIDCKey` (owner only) | | |
| 12 | + | | `TrustedKeyService` | `ListTrustedKeys`, `AddTrustedKey` and `RemoveTrustedKey` (owner only). `Commit.verification` carries the result of the check (feature `commit_signatures`) | | |
| 12 | 13 | | `RunService` | `ListRuns`, `GetRun`, `GetJob`, `GetJobLog` (streamed, follow), `ListAnnotations`, `GetStepSummaries`, `CancelRun`, `RerunRun`, `DispatchWorkflow`, `ListChecks`, `ListRunners`, `RemoveRunner` | | |
| 13 | 14 | | `RunnerService` | `RegisterRunner`, `FetchJob` (long poll), `UpdateJob`, `AppendLog` (offset-idempotent), `Heartbeat`, `WatchJob` (cancel requests, streamed), `CompleteJob` (idempotent), `ResolveActions`, `UploadSummary` | | |
| 14 | 15 | | `SecretService` | `SetSecret`, `ListSecrets` (names only), `DeleteSecret`. Scopes: instance, repository, environment | | |
| @@ -9,6 +9,7 @@ | |||||
| 9 | | `SessionService` | `CreateLoginLink`, `SignOutEverywhere` (owner only) | | 9 | | `SessionService` | `CreateLoginLink`, `SignOutEverywhere` (owner only) | | ||
| 10 | | `EventService` | `Watch` (streamed, resumable) | | 10 | | `EventService` | `Watch` (streamed, resumable) | | ||
| 11 | | `ServerService` | `GetVersion` (version and features), `GetSigningKey`, `RotateOIDCKey` (owner only) | | 11 | | `ServerService` | `GetVersion` (version and features), `GetSigningKey`, `RotateOIDCKey` (owner only) | | ||
| 12 | + | | `TrustedKeyService` | `ListTrustedKeys`, `AddTrustedKey` and `RemoveTrustedKey` (owner only). `Commit.verification` carries the result of the check (feature `commit_signatures`) | | |||
| 12 | | `RunService` | `ListRuns`, `GetRun`, `GetJob`, `GetJobLog` (streamed, follow), `ListAnnotations`, `GetStepSummaries`, `CancelRun`, `RerunRun`, `DispatchWorkflow`, `ListChecks`, `ListRunners`, `RemoveRunner` | | 13 | | `RunService` | `ListRuns`, `GetRun`, `GetJob`, `GetJobLog` (streamed, follow), `ListAnnotations`, `GetStepSummaries`, `CancelRun`, `RerunRun`, `DispatchWorkflow`, `ListChecks`, `ListRunners`, `RemoveRunner` | | ||
| 13 | | `RunnerService` | `RegisterRunner`, `FetchJob` (long poll), `UpdateJob`, `AppendLog` (offset-idempotent), `Heartbeat`, `WatchJob` (cancel requests, streamed), `CompleteJob` (idempotent), `ResolveActions`, `UploadSummary` | | 14 | | `RunnerService` | `RegisterRunner`, `FetchJob` (long poll), `UpdateJob`, `AppendLog` (offset-idempotent), `Heartbeat`, `WatchJob` (cancel requests, streamed), `CompleteJob` (idempotent), `ResolveActions`, `UploadSummary` | | ||
| 14 | | `SecretService` | `SetSecret`, `ListSecrets` (names only), `DeleteSecret`. Scopes: instance, repository, environment | | 15 | | `SecretService` | `SetSecret`, `ListSecrets` (names only), `DeleteSecret`. Scopes: instance, repository, environment | | ||
A gen/ermite/ermiteconnect/signature.connect.go +183 -0
| @@ -0,0 +1,183 @@ | |||
| 1 | + | // SPDX-License-Identifier: MIT | |
| 2 | + | ||
| 3 | + | // Code generated by protoc-gen-connect-go. DO NOT EDIT. | |
| 4 | + | // | |
| 5 | + | // Source: ermite/signature.proto | |
| 6 | + | ||
| 7 | + | package ermiteconnect | |
| 8 | + | ||
| 9 | + | import ( | |
| 10 | + | ermite "bnema.dev/ermite-api/gen/ermite" | |
| 11 | + | connect "connectrpc.com/connect" | |
| 12 | + | context "context" | |
| 13 | + | errors "errors" | |
| 14 | + | http "net/http" | |
| 15 | + | strings "strings" | |
| 16 | + | ) | |
| 17 | + | ||
| 18 | + | // This is a compile-time assertion to ensure that this generated file and the connect package are | |
| 19 | + | // compatible. If you get a compiler error that this constant is not defined, this code was | |
| 20 | + | // generated with a version of connect newer than the one compiled into your binary. You can fix the | |
| 21 | + | // problem by either regenerating this code with an older version of connect or updating the connect | |
| 22 | + | // version compiled into your binary. | |
| 23 | + | const _ = connect.IsAtLeastVersion1_13_0 | |
| 24 | + | ||
| 25 | + | const ( | |
| 26 | + | // TrustedKeyServiceName is the fully-qualified name of the TrustedKeyService service. | |
| 27 | + | TrustedKeyServiceName = "ermite.TrustedKeyService" | |
| 28 | + | ) | |
| 29 | + | ||
| 30 | + | // These constants are the fully-qualified names of the RPCs defined in this package. They're | |
| 31 | + | // exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route. | |
| 32 | + | // | |
| 33 | + | // Note that these are different from the fully-qualified method names used by | |
| 34 | + | // google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to | |
| 35 | + | // reflection-formatted method names, remove the leading slash and convert the remaining slash to a | |
| 36 | + | // period. | |
| 37 | + | const ( | |
| 38 | + | // TrustedKeyServiceListTrustedKeysProcedure is the fully-qualified name of the TrustedKeyService's | |
| 39 | + | // ListTrustedKeys RPC. | |
| 40 | + | TrustedKeyServiceListTrustedKeysProcedure = "/ermite.TrustedKeyService/ListTrustedKeys" | |
| 41 | + | // TrustedKeyServiceAddTrustedKeyProcedure is the fully-qualified name of the TrustedKeyService's | |
| 42 | + | // AddTrustedKey RPC. | |
| 43 | + | TrustedKeyServiceAddTrustedKeyProcedure = "/ermite.TrustedKeyService/AddTrustedKey" | |
| 44 | + | // TrustedKeyServiceRemoveTrustedKeyProcedure is the fully-qualified name of the TrustedKeyService's | |
| 45 | + | // RemoveTrustedKey RPC. | |
| 46 | + | TrustedKeyServiceRemoveTrustedKeyProcedure = "/ermite.TrustedKeyService/RemoveTrustedKey" | |
| 47 | + | ) | |
| 48 | + | ||
| 49 | + | // TrustedKeyServiceClient is a client for the ermite.TrustedKeyService service. | |
| 50 | + | type TrustedKeyServiceClient interface { | |
| 51 | + | // ListTrustedKeys lists the keys, the builtin key first. Any authenticated | |
| 52 | + | // caller may. | |
| 53 | + | ListTrustedKeys(context.Context, *connect.Request[ermite.ListTrustedKeysRequest]) (*connect.Response[ermite.ListTrustedKeysResponse], error) | |
| 54 | + | // AddTrustedKey adds a key. Owner only. invalid_argument for a key that | |
| 55 | + | // does not parse, a private key, no or too many emails; already_exists for | |
| 56 | + | // a key already listed; resource_exhausted beyond 100 keys. | |
| 57 | + | AddTrustedKey(context.Context, *connect.Request[ermite.AddTrustedKeyRequest]) (*connect.Response[ermite.AddTrustedKeyResponse], error) | |
| 58 | + | // RemoveTrustedKey removes a key. Owner only. not_found for an unknown id; | |
| 59 | + | // failed_precondition for the builtin key. | |
| 60 | + | RemoveTrustedKey(context.Context, *connect.Request[ermite.RemoveTrustedKeyRequest]) (*connect.Response[ermite.RemoveTrustedKeyResponse], error) | |
| 61 | + | } | |
| 62 | + | ||
| 63 | + | // NewTrustedKeyServiceClient constructs a client for the ermite.TrustedKeyService service. By | |
| 64 | + | // default, it uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, | |
| 65 | + | // and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the | |
| 66 | + | // connect.WithGRPC() or connect.WithGRPCWeb() options. | |
| 67 | + | // | |
| 68 | + | // The URL supplied here should be the base URL for the Connect or gRPC server (for example, | |
| 69 | + | // http://api.acme.com or https://acme.com/grpc). | |
| 70 | + | func NewTrustedKeyServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) TrustedKeyServiceClient { | |
| 71 | + | baseURL = strings.TrimRight(baseURL, "/") | |
| 72 | + | trustedKeyServiceMethods := ermite.File_ermite_signature_proto.Services().ByName("TrustedKeyService").Methods() | |
| 73 | + | return &trustedKeyServiceClient{ | |
| 74 | + | listTrustedKeys: connect.NewClient[ermite.ListTrustedKeysRequest, ermite.ListTrustedKeysResponse]( | |
| 75 | + | httpClient, | |
| 76 | + | baseURL+TrustedKeyServiceListTrustedKeysProcedure, | |
| 77 | + | connect.WithSchema(trustedKeyServiceMethods.ByName("ListTrustedKeys")), | |
| 78 | + | connect.WithClientOptions(opts...), | |
| 79 | + | ), | |
| 80 | + | addTrustedKey: connect.NewClient[ermite.AddTrustedKeyRequest, ermite.AddTrustedKeyResponse]( | |
| 81 | + | httpClient, | |
| 82 | + | baseURL+TrustedKeyServiceAddTrustedKeyProcedure, | |
| 83 | + | connect.WithSchema(trustedKeyServiceMethods.ByName("AddTrustedKey")), | |
| 84 | + | connect.WithClientOptions(opts...), | |
| 85 | + | ), | |
| 86 | + | removeTrustedKey: connect.NewClient[ermite.RemoveTrustedKeyRequest, ermite.RemoveTrustedKeyResponse]( | |
| 87 | + | httpClient, | |
| 88 | + | baseURL+TrustedKeyServiceRemoveTrustedKeyProcedure, | |
| 89 | + | connect.WithSchema(trustedKeyServiceMethods.ByName("RemoveTrustedKey")), | |
| 90 | + | connect.WithClientOptions(opts...), | |
| 91 | + | ), | |
| 92 | + | } | |
| 93 | + | } | |
| 94 | + | ||
| 95 | + | // trustedKeyServiceClient implements TrustedKeyServiceClient. | |
| 96 | + | type trustedKeyServiceClient struct { | |
| 97 | + | listTrustedKeys *connect.Client[ermite.ListTrustedKeysRequest, ermite.ListTrustedKeysResponse] | |
| 98 | + | addTrustedKey *connect.Client[ermite.AddTrustedKeyRequest, ermite.AddTrustedKeyResponse] | |
| 99 | + | removeTrustedKey *connect.Client[ermite.RemoveTrustedKeyRequest, ermite.RemoveTrustedKeyResponse] | |
| 100 | + | } | |
| 101 | + | ||
| 102 | + | // ListTrustedKeys calls ermite.TrustedKeyService.ListTrustedKeys. | |
| 103 | + | func (c *trustedKeyServiceClient) ListTrustedKeys(ctx context.Context, req *connect.Request[ermite.ListTrustedKeysRequest]) (*connect.Response[ermite.ListTrustedKeysResponse], error) { | |
| 104 | + | return c.listTrustedKeys.CallUnary(ctx, req) | |
| 105 | + | } | |
| 106 | + | ||
| 107 | + | // AddTrustedKey calls ermite.TrustedKeyService.AddTrustedKey. | |
| 108 | + | func (c *trustedKeyServiceClient) AddTrustedKey(ctx context.Context, req *connect.Request[ermite.AddTrustedKeyRequest]) (*connect.Response[ermite.AddTrustedKeyResponse], error) { | |
| 109 | + | return c.addTrustedKey.CallUnary(ctx, req) | |
| 110 | + | } | |
| 111 | + | ||
| 112 | + | // RemoveTrustedKey calls ermite.TrustedKeyService.RemoveTrustedKey. | |
| 113 | + | func (c *trustedKeyServiceClient) RemoveTrustedKey(ctx context.Context, req *connect.Request[ermite.RemoveTrustedKeyRequest]) (*connect.Response[ermite.RemoveTrustedKeyResponse], error) { | |
| 114 | + | return c.removeTrustedKey.CallUnary(ctx, req) | |
| 115 | + | } | |
| 116 | + | ||
| 117 | + | // TrustedKeyServiceHandler is an implementation of the ermite.TrustedKeyService service. | |
| 118 | + | type TrustedKeyServiceHandler interface { | |
| 119 | + | // ListTrustedKeys lists the keys, the builtin key first. Any authenticated | |
| 120 | + | // caller may. | |
| 121 | + | ListTrustedKeys(context.Context, *connect.Request[ermite.ListTrustedKeysRequest]) (*connect.Response[ermite.ListTrustedKeysResponse], error) | |
| 122 | + | // AddTrustedKey adds a key. Owner only. invalid_argument for a key that | |
| 123 | + | // does not parse, a private key, no or too many emails; already_exists for | |
| 124 | + | // a key already listed; resource_exhausted beyond 100 keys. | |
| 125 | + | AddTrustedKey(context.Context, *connect.Request[ermite.AddTrustedKeyRequest]) (*connect.Response[ermite.AddTrustedKeyResponse], error) | |
| 126 | + | // RemoveTrustedKey removes a key. Owner only. not_found for an unknown id; | |
| 127 | + | // failed_precondition for the builtin key. | |
| 128 | + | RemoveTrustedKey(context.Context, *connect.Request[ermite.RemoveTrustedKeyRequest]) (*connect.Response[ermite.RemoveTrustedKeyResponse], error) | |
| 129 | + | } | |
| 130 | + | ||
| 131 | + | // NewTrustedKeyServiceHandler builds an HTTP handler from the service implementation. It returns | |
| 132 | + | // the path on which to mount the handler and the handler itself. | |
| 133 | + | // | |
| 134 | + | // By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf | |
| 135 | + | // and JSON codecs. They also support gzip compression. | |
| 136 | + | func NewTrustedKeyServiceHandler(svc TrustedKeyServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) { | |
| 137 | + | trustedKeyServiceMethods := ermite.File_ermite_signature_proto.Services().ByName("TrustedKeyService").Methods() | |
| 138 | + | trustedKeyServiceListTrustedKeysHandler := connect.NewUnaryHandler( | |
| 139 | + | TrustedKeyServiceListTrustedKeysProcedure, | |
| 140 | + | svc.ListTrustedKeys, | |
| 141 | + | connect.WithSchema(trustedKeyServiceMethods.ByName("ListTrustedKeys")), | |
| 142 | + | connect.WithHandlerOptions(opts...), | |
| 143 | + | ) | |
| 144 | + | trustedKeyServiceAddTrustedKeyHandler := connect.NewUnaryHandler( | |
| 145 | + | TrustedKeyServiceAddTrustedKeyProcedure, | |
| 146 | + | svc.AddTrustedKey, | |
| 147 | + | connect.WithSchema(trustedKeyServiceMethods.ByName("AddTrustedKey")), | |
| 148 | + | connect.WithHandlerOptions(opts...), | |
| 149 | + | ) | |
| 150 | + | trustedKeyServiceRemoveTrustedKeyHandler := connect.NewUnaryHandler( | |
| 151 | + | TrustedKeyServiceRemoveTrustedKeyProcedure, | |
| 152 | + | svc.RemoveTrustedKey, | |
| 153 | + | connect.WithSchema(trustedKeyServiceMethods.ByName("RemoveTrustedKey")), | |
| 154 | + | connect.WithHandlerOptions(opts...), | |
| 155 | + | ) | |
| 156 | + | return "/ermite.TrustedKeyService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |
| 157 | + | switch r.URL.Path { | |
| 158 | + | case TrustedKeyServiceListTrustedKeysProcedure: | |
| 159 | + | trustedKeyServiceListTrustedKeysHandler.ServeHTTP(w, r) | |
| 160 | + | case TrustedKeyServiceAddTrustedKeyProcedure: | |
| 161 | + | trustedKeyServiceAddTrustedKeyHandler.ServeHTTP(w, r) | |
| 162 | + | case TrustedKeyServiceRemoveTrustedKeyProcedure: | |
| 163 | + | trustedKeyServiceRemoveTrustedKeyHandler.ServeHTTP(w, r) | |
| 164 | + | default: | |
| 165 | + | http.NotFound(w, r) | |
| 166 | + | } | |
| 167 | + | }) | |
| 168 | + | } | |
| 169 | + | ||
| 170 | + | // UnimplementedTrustedKeyServiceHandler returns CodeUnimplemented from all methods. | |
| 171 | + | type UnimplementedTrustedKeyServiceHandler struct{} | |
| 172 | + | ||
| 173 | + | func (UnimplementedTrustedKeyServiceHandler) ListTrustedKeys(context.Context, *connect.Request[ermite.ListTrustedKeysRequest]) (*connect.Response[ermite.ListTrustedKeysResponse], error) { | |
| 174 | + | return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.TrustedKeyService.ListTrustedKeys is not implemented")) | |
| 175 | + | } | |
| 176 | + | ||
| 177 | + | func (UnimplementedTrustedKeyServiceHandler) AddTrustedKey(context.Context, *connect.Request[ermite.AddTrustedKeyRequest]) (*connect.Response[ermite.AddTrustedKeyResponse], error) { | |
| 178 | + | return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.TrustedKeyService.AddTrustedKey is not implemented")) | |
| 179 | + | } | |
| 180 | + | ||
| 181 | + | func (UnimplementedTrustedKeyServiceHandler) RemoveTrustedKey(context.Context, *connect.Request[ermite.RemoveTrustedKeyRequest]) (*connect.Response[ermite.RemoveTrustedKeyResponse], error) { | |
| 182 | + | return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.TrustedKeyService.RemoveTrustedKey is not implemented")) | |
| 183 | + | } | |
| @@ -0,0 +1,183 @@ | |||||
| 1 | + | // SPDX-License-Identifier: MIT | |||
| 2 | + | ||||
| 3 | + | // Code generated by protoc-gen-connect-go. DO NOT EDIT. | |||
| 4 | + | // | |||
| 5 | + | // Source: ermite/signature.proto | |||
| 6 | + | ||||
| 7 | + | package ermiteconnect | |||
| 8 | + | ||||
| 9 | + | import ( | |||
| 10 | + | ermite "bnema.dev/ermite-api/gen/ermite" | |||
| 11 | + | connect "connectrpc.com/connect" | |||
| 12 | + | context "context" | |||
| 13 | + | errors "errors" | |||
| 14 | + | http "net/http" | |||
| 15 | + | strings "strings" | |||
| 16 | + | ) | |||
| 17 | + | ||||
| 18 | + | // This is a compile-time assertion to ensure that this generated file and the connect package are | |||
| 19 | + | // compatible. If you get a compiler error that this constant is not defined, this code was | |||
| 20 | + | // generated with a version of connect newer than the one compiled into your binary. You can fix the | |||
| 21 | + | // problem by either regenerating this code with an older version of connect or updating the connect | |||
| 22 | + | // version compiled into your binary. | |||
| 23 | + | const _ = connect.IsAtLeastVersion1_13_0 | |||
| 24 | + | ||||
| 25 | + | const ( | |||
| 26 | + | // TrustedKeyServiceName is the fully-qualified name of the TrustedKeyService service. | |||
| 27 | + | TrustedKeyServiceName = "ermite.TrustedKeyService" | |||
| 28 | + | ) | |||
| 29 | + | ||||
| 30 | + | // These constants are the fully-qualified names of the RPCs defined in this package. They're | |||
| 31 | + | // exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route. | |||
| 32 | + | // | |||
| 33 | + | // Note that these are different from the fully-qualified method names used by | |||
| 34 | + | // google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to | |||
| 35 | + | // reflection-formatted method names, remove the leading slash and convert the remaining slash to a | |||
| 36 | + | // period. | |||
| 37 | + | const ( | |||
| 38 | + | // TrustedKeyServiceListTrustedKeysProcedure is the fully-qualified name of the TrustedKeyService's | |||
| 39 | + | // ListTrustedKeys RPC. | |||
| 40 | + | TrustedKeyServiceListTrustedKeysProcedure = "/ermite.TrustedKeyService/ListTrustedKeys" | |||
| 41 | + | // TrustedKeyServiceAddTrustedKeyProcedure is the fully-qualified name of the TrustedKeyService's | |||
| 42 | + | // AddTrustedKey RPC. | |||
| 43 | + | TrustedKeyServiceAddTrustedKeyProcedure = "/ermite.TrustedKeyService/AddTrustedKey" | |||
| 44 | + | // TrustedKeyServiceRemoveTrustedKeyProcedure is the fully-qualified name of the TrustedKeyService's | |||
| 45 | + | // RemoveTrustedKey RPC. | |||
| 46 | + | TrustedKeyServiceRemoveTrustedKeyProcedure = "/ermite.TrustedKeyService/RemoveTrustedKey" | |||
| 47 | + | ) | |||
| 48 | + | ||||
| 49 | + | // TrustedKeyServiceClient is a client for the ermite.TrustedKeyService service. | |||
| 50 | + | type TrustedKeyServiceClient interface { | |||
| 51 | + | // ListTrustedKeys lists the keys, the builtin key first. Any authenticated | |||
| 52 | + | // caller may. | |||
| 53 | + | ListTrustedKeys(context.Context, *connect.Request[ermite.ListTrustedKeysRequest]) (*connect.Response[ermite.ListTrustedKeysResponse], error) | |||
| 54 | + | // AddTrustedKey adds a key. Owner only. invalid_argument for a key that | |||
| 55 | + | // does not parse, a private key, no or too many emails; already_exists for | |||
| 56 | + | // a key already listed; resource_exhausted beyond 100 keys. | |||
| 57 | + | AddTrustedKey(context.Context, *connect.Request[ermite.AddTrustedKeyRequest]) (*connect.Response[ermite.AddTrustedKeyResponse], error) | |||
| 58 | + | // RemoveTrustedKey removes a key. Owner only. not_found for an unknown id; | |||
| 59 | + | // failed_precondition for the builtin key. | |||
| 60 | + | RemoveTrustedKey(context.Context, *connect.Request[ermite.RemoveTrustedKeyRequest]) (*connect.Response[ermite.RemoveTrustedKeyResponse], error) | |||
| 61 | + | } | |||
| 62 | + | ||||
| 63 | + | // NewTrustedKeyServiceClient constructs a client for the ermite.TrustedKeyService service. By | |||
| 64 | + | // default, it uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, | |||
| 65 | + | // and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the | |||
| 66 | + | // connect.WithGRPC() or connect.WithGRPCWeb() options. | |||
| 67 | + | // | |||
| 68 | + | // The URL supplied here should be the base URL for the Connect or gRPC server (for example, | |||
| 69 | + | // http://api.acme.com or https://acme.com/grpc). | |||
| 70 | + | func NewTrustedKeyServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) TrustedKeyServiceClient { | |||
| 71 | + | baseURL = strings.TrimRight(baseURL, "/") | |||
| 72 | + | trustedKeyServiceMethods := ermite.File_ermite_signature_proto.Services().ByName("TrustedKeyService").Methods() | |||
| 73 | + | return &trustedKeyServiceClient{ | |||
| 74 | + | listTrustedKeys: connect.NewClient[ermite.ListTrustedKeysRequest, ermite.ListTrustedKeysResponse]( | |||
| 75 | + | httpClient, | |||
| 76 | + | baseURL+TrustedKeyServiceListTrustedKeysProcedure, | |||
| 77 | + | connect.WithSchema(trustedKeyServiceMethods.ByName("ListTrustedKeys")), | |||
| 78 | + | connect.WithClientOptions(opts...), | |||
| 79 | + | ), | |||
| 80 | + | addTrustedKey: connect.NewClient[ermite.AddTrustedKeyRequest, ermite.AddTrustedKeyResponse]( | |||
| 81 | + | httpClient, | |||
| 82 | + | baseURL+TrustedKeyServiceAddTrustedKeyProcedure, | |||
| 83 | + | connect.WithSchema(trustedKeyServiceMethods.ByName("AddTrustedKey")), | |||
| 84 | + | connect.WithClientOptions(opts...), | |||
| 85 | + | ), | |||
| 86 | + | removeTrustedKey: connect.NewClient[ermite.RemoveTrustedKeyRequest, ermite.RemoveTrustedKeyResponse]( | |||
| 87 | + | httpClient, | |||
| 88 | + | baseURL+TrustedKeyServiceRemoveTrustedKeyProcedure, | |||
| 89 | + | connect.WithSchema(trustedKeyServiceMethods.ByName("RemoveTrustedKey")), | |||
| 90 | + | connect.WithClientOptions(opts...), | |||
| 91 | + | ), | |||
| 92 | + | } | |||
| 93 | + | } | |||
| 94 | + | ||||
| 95 | + | // trustedKeyServiceClient implements TrustedKeyServiceClient. | |||
| 96 | + | type trustedKeyServiceClient struct { | |||
| 97 | + | listTrustedKeys *connect.Client[ermite.ListTrustedKeysRequest, ermite.ListTrustedKeysResponse] | |||
| 98 | + | addTrustedKey *connect.Client[ermite.AddTrustedKeyRequest, ermite.AddTrustedKeyResponse] | |||
| 99 | + | removeTrustedKey *connect.Client[ermite.RemoveTrustedKeyRequest, ermite.RemoveTrustedKeyResponse] | |||
| 100 | + | } | |||
| 101 | + | ||||
| 102 | + | // ListTrustedKeys calls ermite.TrustedKeyService.ListTrustedKeys. | |||
| 103 | + | func (c *trustedKeyServiceClient) ListTrustedKeys(ctx context.Context, req *connect.Request[ermite.ListTrustedKeysRequest]) (*connect.Response[ermite.ListTrustedKeysResponse], error) { | |||
| 104 | + | return c.listTrustedKeys.CallUnary(ctx, req) | |||
| 105 | + | } | |||
| 106 | + | ||||
| 107 | + | // AddTrustedKey calls ermite.TrustedKeyService.AddTrustedKey. | |||
| 108 | + | func (c *trustedKeyServiceClient) AddTrustedKey(ctx context.Context, req *connect.Request[ermite.AddTrustedKeyRequest]) (*connect.Response[ermite.AddTrustedKeyResponse], error) { | |||
| 109 | + | return c.addTrustedKey.CallUnary(ctx, req) | |||
| 110 | + | } | |||
| 111 | + | ||||
| 112 | + | // RemoveTrustedKey calls ermite.TrustedKeyService.RemoveTrustedKey. | |||
| 113 | + | func (c *trustedKeyServiceClient) RemoveTrustedKey(ctx context.Context, req *connect.Request[ermite.RemoveTrustedKeyRequest]) (*connect.Response[ermite.RemoveTrustedKeyResponse], error) { | |||
| 114 | + | return c.removeTrustedKey.CallUnary(ctx, req) | |||
| 115 | + | } | |||
| 116 | + | ||||
| 117 | + | // TrustedKeyServiceHandler is an implementation of the ermite.TrustedKeyService service. | |||
| 118 | + | type TrustedKeyServiceHandler interface { | |||
| 119 | + | // ListTrustedKeys lists the keys, the builtin key first. Any authenticated | |||
| 120 | + | // caller may. | |||
| 121 | + | ListTrustedKeys(context.Context, *connect.Request[ermite.ListTrustedKeysRequest]) (*connect.Response[ermite.ListTrustedKeysResponse], error) | |||
| 122 | + | // AddTrustedKey adds a key. Owner only. invalid_argument for a key that | |||
| 123 | + | // does not parse, a private key, no or too many emails; already_exists for | |||
| 124 | + | // a key already listed; resource_exhausted beyond 100 keys. | |||
| 125 | + | AddTrustedKey(context.Context, *connect.Request[ermite.AddTrustedKeyRequest]) (*connect.Response[ermite.AddTrustedKeyResponse], error) | |||
| 126 | + | // RemoveTrustedKey removes a key. Owner only. not_found for an unknown id; | |||
| 127 | + | // failed_precondition for the builtin key. | |||
| 128 | + | RemoveTrustedKey(context.Context, *connect.Request[ermite.RemoveTrustedKeyRequest]) (*connect.Response[ermite.RemoveTrustedKeyResponse], error) | |||
| 129 | + | } | |||
| 130 | + | ||||
| 131 | + | // NewTrustedKeyServiceHandler builds an HTTP handler from the service implementation. It returns | |||
| 132 | + | // the path on which to mount the handler and the handler itself. | |||
| 133 | + | // | |||
| 134 | + | // By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf | |||
| 135 | + | // and JSON codecs. They also support gzip compression. | |||
| 136 | + | func NewTrustedKeyServiceHandler(svc TrustedKeyServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) { | |||
| 137 | + | trustedKeyServiceMethods := ermite.File_ermite_signature_proto.Services().ByName("TrustedKeyService").Methods() | |||
| 138 | + | trustedKeyServiceListTrustedKeysHandler := connect.NewUnaryHandler( | |||
| 139 | + | TrustedKeyServiceListTrustedKeysProcedure, | |||
| 140 | + | svc.ListTrustedKeys, | |||
| 141 | + | connect.WithSchema(trustedKeyServiceMethods.ByName("ListTrustedKeys")), | |||
| 142 | + | connect.WithHandlerOptions(opts...), | |||
| 143 | + | ) | |||
| 144 | + | trustedKeyServiceAddTrustedKeyHandler := connect.NewUnaryHandler( | |||
| 145 | + | TrustedKeyServiceAddTrustedKeyProcedure, | |||
| 146 | + | svc.AddTrustedKey, | |||
| 147 | + | connect.WithSchema(trustedKeyServiceMethods.ByName("AddTrustedKey")), | |||
| 148 | + | connect.WithHandlerOptions(opts...), | |||
| 149 | + | ) | |||
| 150 | + | trustedKeyServiceRemoveTrustedKeyHandler := connect.NewUnaryHandler( | |||
| 151 | + | TrustedKeyServiceRemoveTrustedKeyProcedure, | |||
| 152 | + | svc.RemoveTrustedKey, | |||
| 153 | + | connect.WithSchema(trustedKeyServiceMethods.ByName("RemoveTrustedKey")), | |||
| 154 | + | connect.WithHandlerOptions(opts...), | |||
| 155 | + | ) | |||
| 156 | + | return "/ermite.TrustedKeyService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |||
| 157 | + | switch r.URL.Path { | |||
| 158 | + | case TrustedKeyServiceListTrustedKeysProcedure: | |||
| 159 | + | trustedKeyServiceListTrustedKeysHandler.ServeHTTP(w, r) | |||
| 160 | + | case TrustedKeyServiceAddTrustedKeyProcedure: | |||
| 161 | + | trustedKeyServiceAddTrustedKeyHandler.ServeHTTP(w, r) | |||
| 162 | + | case TrustedKeyServiceRemoveTrustedKeyProcedure: | |||
| 163 | + | trustedKeyServiceRemoveTrustedKeyHandler.ServeHTTP(w, r) | |||
| 164 | + | default: | |||
| 165 | + | http.NotFound(w, r) | |||
| 166 | + | } | |||
| 167 | + | }) | |||
| 168 | + | } | |||
| 169 | + | ||||
| 170 | + | // UnimplementedTrustedKeyServiceHandler returns CodeUnimplemented from all methods. | |||
| 171 | + | type UnimplementedTrustedKeyServiceHandler struct{} | |||
| 172 | + | ||||
| 173 | + | func (UnimplementedTrustedKeyServiceHandler) ListTrustedKeys(context.Context, *connect.Request[ermite.ListTrustedKeysRequest]) (*connect.Response[ermite.ListTrustedKeysResponse], error) { | |||
| 174 | + | return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.TrustedKeyService.ListTrustedKeys is not implemented")) | |||
| 175 | + | } | |||
| 176 | + | ||||
| 177 | + | func (UnimplementedTrustedKeyServiceHandler) AddTrustedKey(context.Context, *connect.Request[ermite.AddTrustedKeyRequest]) (*connect.Response[ermite.AddTrustedKeyResponse], error) { | |||
| 178 | + | return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.TrustedKeyService.AddTrustedKey is not implemented")) | |||
| 179 | + | } | |||
| 180 | + | ||||
| 181 | + | func (UnimplementedTrustedKeyServiceHandler) RemoveTrustedKey(context.Context, *connect.Request[ermite.RemoveTrustedKeyRequest]) (*connect.Response[ermite.RemoveTrustedKeyResponse], error) { | |||
| 182 | + | return nil, connect.NewError(connect.CodeUnimplemented, errors.New("ermite.TrustedKeyService.RemoveTrustedKey is not implemented")) | |||
| 183 | + | } | |||
A gen/ermite/signature.pb.go +759 -0 generated file: collapsed
M gen/ermite/types.pb.go +23 -9 generated file: collapsed
A proto/ermite/signature.proto +128 -0
| @@ -0,0 +1,128 @@ | |||
| 1 | + | // SPDX-License-Identifier: MIT | |
| 2 | + | ||
| 3 | + | syntax = "proto3"; | |
| 4 | + | ||
| 5 | + | package ermite; | |
| 6 | + | ||
| 7 | + | import "google/protobuf/timestamp.proto"; | |
| 8 | + | ||
| 9 | + | // SignatureFormat is the protocol of a commit signature. | |
| 10 | + | enum SignatureFormat { | |
| 11 | + | // Not signed, or not set. | |
| 12 | + | SIGNATURE_FORMAT_UNSPECIFIED = 0; | |
| 13 | + | // Git SSH signature (gpg.format=ssh, "-----BEGIN SSH SIGNATURE-----"). | |
| 14 | + | SIGNATURE_FORMAT_SSH = 1; | |
| 15 | + | // OpenPGP signature (gpg.format=openpgp, "-----BEGIN PGP SIGNATURE-----"). | |
| 16 | + | SIGNATURE_FORMAT_GPG = 2; | |
| 17 | + | // X.509 (gpgsm) signature. Recognized, never verified. | |
| 18 | + | SIGNATURE_FORMAT_X509 = 3; | |
| 19 | + | } | |
| 20 | + | ||
| 21 | + | // SignatureStatus is the outcome of checking the signature of a commit. | |
| 22 | + | enum SignatureStatus { | |
| 23 | + | // The server did not check the commit (it could not be read). | |
| 24 | + | SIGNATURE_STATUS_UNSPECIFIED = 0; | |
| 25 | + | // The commit carries no signature. | |
| 26 | + | SIGNATURE_STATUS_UNSIGNED = 1; | |
| 27 | + | // Valid signature by a trusted key that is trusted for the committer email. | |
| 28 | + | SIGNATURE_STATUS_VERIFIED = 2; | |
| 29 | + | // Well-formed signature by a key the server does not trust. | |
| 30 | + | SIGNATURE_STATUS_UNKNOWN_KEY = 3; | |
| 31 | + | // The signature is malformed, does not match the commit, or uses a weak | |
| 32 | + | // hash (SHA-1 and older). | |
| 33 | + | SIGNATURE_STATUS_BAD_SIGNATURE = 4; | |
| 34 | + | // Valid signature by a trusted key that is not trusted for the committer | |
| 35 | + | // email. | |
| 36 | + | SIGNATURE_STATUS_EMAIL_MISMATCH = 5; | |
| 37 | + | // The OpenPGP key or signature had expired when the signature was made. | |
| 38 | + | SIGNATURE_STATUS_EXPIRED = 6; | |
| 39 | + | // The OpenPGP key is revoked. | |
| 40 | + | SIGNATURE_STATUS_REVOKED = 7; | |
| 41 | + | // The format is not verified by the server (X.509). | |
| 42 | + | SIGNATURE_STATUS_UNSUPPORTED = 8; | |
| 43 | + | } | |
| 44 | + | ||
| 45 | + | // CommitVerification tells whether and how a commit is signed. | |
| 46 | + | message CommitVerification { | |
| 47 | + | SignatureFormat format = 1; | |
| 48 | + | SignatureStatus status = 2; | |
| 49 | + | // Fingerprint of the signing key when the signature names one: | |
| 50 | + | // "SHA256:<base64>" for SSH, upper-case hex for OpenPGP (the 16-digit | |
| 51 | + | // issuer key id when the key is unknown). | |
| 52 | + | string fingerprint = 3; | |
| 53 | + | // KeyId is the id of the trusted key that matched; empty when none did. | |
| 54 | + | string key_id = 4; | |
| 55 | + | // KeyTitle is the title of the trusted key that matched. | |
| 56 | + | string key_title = 5; | |
| 57 | + | } | |
| 58 | + | ||
| 59 | + | // TrustedKey is a public key whose signatures the server accepts for the | |
| 60 | + | // listed committer emails. | |
| 61 | + | message TrustedKey { | |
| 62 | + | // Id is the key fingerprint: "SHA256:<base64>" for SSH, the primary key | |
| 63 | + | // fingerprint in upper-case hex for OpenPGP. | |
| 64 | + | string id = 1; | |
| 65 | + | // Format is SIGNATURE_FORMAT_SSH or SIGNATURE_FORMAT_GPG. | |
| 66 | + | SignatureFormat format = 2; | |
| 67 | + | string title = 3; | |
| 68 | + | // PublicKey is "<type> <base64>" for SSH, the armored public key for | |
| 69 | + | // OpenPGP. | |
| 70 | + | string public_key = 4; | |
| 71 | + | // Emails are the committer emails the key may sign for, lower-case. | |
| 72 | + | repeated string emails = 5; | |
| 73 | + | google.protobuf.Timestamp created_at = 6; | |
| 74 | + | // Builtin is true for the server's own signing key | |
| 75 | + | // (ServerService.GetSigningKey): always trusted, cannot be removed. | |
| 76 | + | bool builtin = 7; | |
| 77 | + | } | |
| 78 | + | ||
| 79 | + | // TrustedKeyService manages the keys commit signatures are checked against. | |
| 80 | + | // Servers that implement it list "commit_signatures" in | |
| 81 | + | // GetVersionResponse.features; their Commit messages then carry | |
| 82 | + | // verification. | |
| 83 | + | service TrustedKeyService { | |
| 84 | + | // ListTrustedKeys lists the keys, the builtin key first. Any authenticated | |
| 85 | + | // caller may. | |
| 86 | + | rpc ListTrustedKeys(ListTrustedKeysRequest) returns (ListTrustedKeysResponse); | |
| 87 | + | // AddTrustedKey adds a key. Owner only. invalid_argument for a key that | |
| 88 | + | // does not parse, a private key, no or too many emails; already_exists for | |
| 89 | + | // a key already listed; resource_exhausted beyond 100 keys. | |
| 90 | + | rpc AddTrustedKey(AddTrustedKeyRequest) returns (AddTrustedKeyResponse); | |
| 91 | + | // RemoveTrustedKey removes a key. Owner only. not_found for an unknown id; | |
| 92 | + | // failed_precondition for the builtin key. | |
| 93 | + | rpc RemoveTrustedKey(RemoveTrustedKeyRequest) returns (RemoveTrustedKeyResponse); | |
| 94 | + | } | |
| 95 | + | ||
| 96 | + | // ListTrustedKeysRequest is empty. | |
| 97 | + | message ListTrustedKeysRequest {} | |
| 98 | + | ||
| 99 | + | // ListTrustedKeysResponse lists every trusted key. | |
| 100 | + | message ListTrustedKeysResponse { | |
| 101 | + | repeated TrustedKey keys = 1; | |
| 102 | + | } | |
| 103 | + | ||
| 104 | + | // AddTrustedKeyRequest describes the key to trust. | |
| 105 | + | message AddTrustedKeyRequest { | |
| 106 | + | // Format is SIGNATURE_FORMAT_SSH or SIGNATURE_FORMAT_GPG. | |
| 107 | + | SignatureFormat format = 1; | |
| 108 | + | // Title is a label, 1 to 100 bytes. | |
| 109 | + | string title = 2; | |
| 110 | + | // PublicKey is an authorized_keys line for SSH, or an armored public key | |
| 111 | + | // block for OpenPGP (at most 64 KiB). | |
| 112 | + | string public_key = 3; | |
| 113 | + | // Emails are the committer emails the key may sign for, 1 to 10. | |
| 114 | + | repeated string emails = 4; | |
| 115 | + | } | |
| 116 | + | ||
| 117 | + | // AddTrustedKeyResponse returns the stored key. | |
| 118 | + | message AddTrustedKeyResponse { | |
| 119 | + | TrustedKey key = 1; | |
| 120 | + | } | |
| 121 | + | ||
| 122 | + | // RemoveTrustedKeyRequest names the key to remove. | |
| 123 | + | message RemoveTrustedKeyRequest { | |
| 124 | + | string id = 1; | |
| 125 | + | } | |
| 126 | + | ||
| 127 | + | // RemoveTrustedKeyResponse is empty. | |
| 128 | + | message RemoveTrustedKeyResponse {} | |
| @@ -0,0 +1,128 @@ | |||||
| 1 | + | // SPDX-License-Identifier: MIT | |||
| 2 | + | ||||
| 3 | + | syntax = "proto3"; | |||
| 4 | + | ||||
| 5 | + | package ermite; | |||
| 6 | + | ||||
| 7 | + | import "google/protobuf/timestamp.proto"; | |||
| 8 | + | ||||
| 9 | + | // SignatureFormat is the protocol of a commit signature. | |||
| 10 | + | enum SignatureFormat { | |||
| 11 | + | // Not signed, or not set. | |||
| 12 | + | SIGNATURE_FORMAT_UNSPECIFIED = 0; | |||
| 13 | + | // Git SSH signature (gpg.format=ssh, "-----BEGIN SSH SIGNATURE-----"). | |||
| 14 | + | SIGNATURE_FORMAT_SSH = 1; | |||
| 15 | + | // OpenPGP signature (gpg.format=openpgp, "-----BEGIN PGP SIGNATURE-----"). | |||
| 16 | + | SIGNATURE_FORMAT_GPG = 2; | |||
| 17 | + | // X.509 (gpgsm) signature. Recognized, never verified. | |||
| 18 | + | SIGNATURE_FORMAT_X509 = 3; | |||
| 19 | + | } | |||
| 20 | + | ||||
| 21 | + | // SignatureStatus is the outcome of checking the signature of a commit. | |||
| 22 | + | enum SignatureStatus { | |||
| 23 | + | // The server did not check the commit (it could not be read). | |||
| 24 | + | SIGNATURE_STATUS_UNSPECIFIED = 0; | |||
| 25 | + | // The commit carries no signature. | |||
| 26 | + | SIGNATURE_STATUS_UNSIGNED = 1; | |||
| 27 | + | // Valid signature by a trusted key that is trusted for the committer email. | |||
| 28 | + | SIGNATURE_STATUS_VERIFIED = 2; | |||
| 29 | + | // Well-formed signature by a key the server does not trust. | |||
| 30 | + | SIGNATURE_STATUS_UNKNOWN_KEY = 3; | |||
| 31 | + | // The signature is malformed, does not match the commit, or uses a weak | |||
| 32 | + | // hash (SHA-1 and older). | |||
| 33 | + | SIGNATURE_STATUS_BAD_SIGNATURE = 4; | |||
| 34 | + | // Valid signature by a trusted key that is not trusted for the committer | |||
| 35 | + | // email. | |||
| 36 | + | SIGNATURE_STATUS_EMAIL_MISMATCH = 5; | |||
| 37 | + | // The OpenPGP key or signature had expired when the signature was made. | |||
| 38 | + | SIGNATURE_STATUS_EXPIRED = 6; | |||
| 39 | + | // The OpenPGP key is revoked. | |||
| 40 | + | SIGNATURE_STATUS_REVOKED = 7; | |||
| 41 | + | // The format is not verified by the server (X.509). | |||
| 42 | + | SIGNATURE_STATUS_UNSUPPORTED = 8; | |||
| 43 | + | } | |||
| 44 | + | ||||
| 45 | + | // CommitVerification tells whether and how a commit is signed. | |||
| 46 | + | message CommitVerification { | |||
| 47 | + | SignatureFormat format = 1; | |||
| 48 | + | SignatureStatus status = 2; | |||
| 49 | + | // Fingerprint of the signing key when the signature names one: | |||
| 50 | + | // "SHA256:<base64>" for SSH, upper-case hex for OpenPGP (the 16-digit | |||
| 51 | + | // issuer key id when the key is unknown). | |||
| 52 | + | string fingerprint = 3; | |||
| 53 | + | // KeyId is the id of the trusted key that matched; empty when none did. | |||
| 54 | + | string key_id = 4; | |||
| 55 | + | // KeyTitle is the title of the trusted key that matched. | |||
| 56 | + | string key_title = 5; | |||
| 57 | + | } | |||
| 58 | + | ||||
| 59 | + | // TrustedKey is a public key whose signatures the server accepts for the | |||
| 60 | + | // listed committer emails. | |||
| 61 | + | message TrustedKey { | |||
| 62 | + | // Id is the key fingerprint: "SHA256:<base64>" for SSH, the primary key | |||
| 63 | + | // fingerprint in upper-case hex for OpenPGP. | |||
| 64 | + | string id = 1; | |||
| 65 | + | // Format is SIGNATURE_FORMAT_SSH or SIGNATURE_FORMAT_GPG. | |||
| 66 | + | SignatureFormat format = 2; | |||
| 67 | + | string title = 3; | |||
| 68 | + | // PublicKey is "<type> <base64>" for SSH, the armored public key for | |||
| 69 | + | // OpenPGP. | |||
| 70 | + | string public_key = 4; | |||
| 71 | + | // Emails are the committer emails the key may sign for, lower-case. | |||
| 72 | + | repeated string emails = 5; | |||
| 73 | + | google.protobuf.Timestamp created_at = 6; | |||
| 74 | + | // Builtin is true for the server's own signing key | |||
| 75 | + | // (ServerService.GetSigningKey): always trusted, cannot be removed. | |||
| 76 | + | bool builtin = 7; | |||
| 77 | + | } | |||
| 78 | + | ||||
| 79 | + | // TrustedKeyService manages the keys commit signatures are checked against. | |||
| 80 | + | // Servers that implement it list "commit_signatures" in | |||
| 81 | + | // GetVersionResponse.features; their Commit messages then carry | |||
| 82 | + | // verification. | |||
| 83 | + | service TrustedKeyService { | |||
| 84 | + | // ListTrustedKeys lists the keys, the builtin key first. Any authenticated | |||
| 85 | + | // caller may. | |||
| 86 | + | rpc ListTrustedKeys(ListTrustedKeysRequest) returns (ListTrustedKeysResponse); | |||
| 87 | + | // AddTrustedKey adds a key. Owner only. invalid_argument for a key that | |||
| 88 | + | // does not parse, a private key, no or too many emails; already_exists for | |||
| 89 | + | // a key already listed; resource_exhausted beyond 100 keys. | |||
| 90 | + | rpc AddTrustedKey(AddTrustedKeyRequest) returns (AddTrustedKeyResponse); | |||
| 91 | + | // RemoveTrustedKey removes a key. Owner only. not_found for an unknown id; | |||
| 92 | + | // failed_precondition for the builtin key. | |||
| 93 | + | rpc RemoveTrustedKey(RemoveTrustedKeyRequest) returns (RemoveTrustedKeyResponse); | |||
| 94 | + | } | |||
| 95 | + | ||||
| 96 | + | // ListTrustedKeysRequest is empty. | |||
| 97 | + | message ListTrustedKeysRequest {} | |||
| 98 | + | ||||
| 99 | + | // ListTrustedKeysResponse lists every trusted key. | |||
| 100 | + | message ListTrustedKeysResponse { | |||
| 101 | + | repeated TrustedKey keys = 1; | |||
| 102 | + | } | |||
| 103 | + | ||||
| 104 | + | // AddTrustedKeyRequest describes the key to trust. | |||
| 105 | + | message AddTrustedKeyRequest { | |||
| 106 | + | // Format is SIGNATURE_FORMAT_SSH or SIGNATURE_FORMAT_GPG. | |||
| 107 | + | SignatureFormat format = 1; | |||
| 108 | + | // Title is a label, 1 to 100 bytes. | |||
| 109 | + | string title = 2; | |||
| 110 | + | // PublicKey is an authorized_keys line for SSH, or an armored public key | |||
| 111 | + | // block for OpenPGP (at most 64 KiB). | |||
| 112 | + | string public_key = 3; | |||
| 113 | + | // Emails are the committer emails the key may sign for, 1 to 10. | |||
| 114 | + | repeated string emails = 4; | |||
| 115 | + | } | |||
| 116 | + | ||||
| 117 | + | // AddTrustedKeyResponse returns the stored key. | |||
| 118 | + | message AddTrustedKeyResponse { | |||
| 119 | + | TrustedKey key = 1; | |||
| 120 | + | } | |||
| 121 | + | ||||
| 122 | + | // RemoveTrustedKeyRequest names the key to remove. | |||
| 123 | + | message RemoveTrustedKeyRequest { | |||
| 124 | + | string id = 1; | |||
| 125 | + | } | |||
| 126 | + | ||||
| 127 | + | // RemoveTrustedKeyResponse is empty. | |||
| 128 | + | message RemoveTrustedKeyResponse {} | |||
M proto/ermite/types.proto +4 -0
| @@ -4,6 +4,7 @@ syntax = "proto3"; | |||
| 4 | 4 | ||
| 5 | 5 | package ermite; | |
| 6 | 6 | ||
| 7 | + | import "ermite/signature.proto"; | |
| 7 | 8 | import "google/protobuf/timestamp.proto"; | |
| 8 | 9 | ||
| 9 | 10 | // Signature identifies who did something in Git and when: the author or the | |
| @@ -34,6 +35,9 @@ message Commit { | |||
| 34 | 35 | // Message is the full commit message, including the summary. Text that is | |
| 35 | 36 | // not valid UTF-8 is converted with the replacement character. | |
| 36 | 37 | string message = 6; | |
| 38 | + | // Verification tells whether and how the commit is signed. Unset on | |
| 39 | + | // servers without the "commit_signatures" feature. | |
| 40 | + | CommitVerification verification = 7; | |
| 37 | 41 | } | |
| 38 | 42 | ||
| 39 | 43 | // MergeMode is how a merge request is merged. Both modes need the target to | |
| @@ -4,6 +4,7 @@ syntax = "proto3"; | |||||
| 4 | 4 | ||||
| 5 | package ermite; | 5 | package ermite; | ||
| 6 | 6 | ||||
| 7 | + | import "ermite/signature.proto"; | |||
| 7 | import "google/protobuf/timestamp.proto"; | 8 | import "google/protobuf/timestamp.proto"; | ||
| 8 | 9 | ||||
| 9 | // Signature identifies who did something in Git and when: the author or the | 10 | // Signature identifies who did something in Git and when: the author or the | ||
| @@ -34,6 +35,9 @@ message Commit { | |||||
| 34 | // Message is the full commit message, including the summary. Text that is | 35 | // Message is the full commit message, including the summary. Text that is | ||
| 35 | // not valid UTF-8 is converted with the replacement character. | 36 | // not valid UTF-8 is converted with the replacement character. | ||
| 36 | string message = 6; | 37 | string message = 6; | ||
| 38 | + | // Verification tells whether and how the commit is signed. Unset on | |||
| 39 | + | // servers without the "commit_signatures" feature. | |||
| 40 | + | CommitVerification verification = 7; | |||
| 37 | } | 41 | } | ||
| 38 | 42 | ||||
| 39 | // MergeMode is how a merge request is merged. Both modes need the target to | 43 | // MergeMode is how a merge request is merged. Both modes need the target to | ||