diff --git a/buf.gen.openapi.yaml b/buf.gen.openapi.yaml index acdd641..ff1ba3d 100644 --- a/buf.gen.openapi.yaml +++ b/buf.gen.openapi.yaml @@ -1,13 +1,12 @@ version: v2 plugins: - # Generate OpenAPI spec for ConnectRPC with protoc-gen-connect-openapi - # Install: go install github.com/sudorandom/protoc-gen-connect-openapi/cmd/protoc-gen-connect-openapi@latest - - local: protoc-gen-connect-openapi + # Generate OpenAPI spec for ConnectRPC with a version-pinned Buf plugin. + # API metadata is applied by api/scripts/inject-oauth-scopes.py because + # remote plugins intentionally disable local base files. + - remote: buf.build/community/sudorandom-connect-openapi:v0.21.3 out: ../api/openapi - strategy: all opt: - format=json - - base=openapi-base.yaml - - path=openapi.yaml + - path=openapi.internal.yaml - features=connectrpc;gnostic;protovalidate - allow-get diff --git a/cmd/filter-public-openapi/main.go b/cmd/filter-public-openapi/main.go new file mode 100644 index 0000000..50adaa0 --- /dev/null +++ b/cmd/filter-public-openapi/main.go @@ -0,0 +1,34 @@ +package main + +import ( + "flag" + "fmt" + "os" + + "github.com/libops/proto/internal/openapivisibility" +) + +func main() { + inputPath := flag.String("input", "", "complete generated OpenAPI document") + outputPath := flag.String("output", "", "customer OpenAPI document") + flag.Parse() + if *inputPath == "" || *outputPath == "" { + fmt.Fprintln(os.Stderr, "both -input and -output are required") + os.Exit(2) + } + + input, err := os.ReadFile(*inputPath) + if err != nil { + fmt.Fprintf(os.Stderr, "read %s: %v\n", *inputPath, err) + os.Exit(1) + } + output, err := openapivisibility.Filter(input) + if err != nil { + fmt.Fprintf(os.Stderr, "filter %s: %v\n", *inputPath, err) + os.Exit(1) + } + if err := os.WriteFile(*outputPath, output, 0o644); err != nil { + fmt.Fprintf(os.Stderr, "write %s: %v\n", *outputPath, err) + os.Exit(1) + } +} diff --git a/internal/openapivisibility/filter.go b/internal/openapivisibility/filter.go new file mode 100644 index 0000000..bc73fd7 --- /dev/null +++ b/internal/openapivisibility/filter.go @@ -0,0 +1,282 @@ +// Package openapivisibility derives the customer OpenAPI document from the +// complete generated document and protobuf visibility annotations. +package openapivisibility + +import ( + "encoding/json" + "fmt" + "sort" + "strings" + + _ "github.com/libops/proto/libops/v1" + optionsv1 "github.com/libops/proto/libops/v1/options" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" + "google.golang.org/protobuf/types/descriptorpb" +) + +var httpMethods = map[string]bool{ + "delete": true, + "get": true, + "head": true, + "options": true, + "patch": true, + "post": true, + "put": true, + "trace": true, +} + +// Filter returns a public OpenAPI document. Unknown or unannotated operations +// are errors so a new RPC cannot become customer-visible by accident. +func Filter(input []byte) ([]byte, error) { + var document map[string]any + if err := json.Unmarshal(input, &document); err != nil { + return nil, fmt.Errorf("decode OpenAPI document: %w", err) + } + + paths, ok := document["paths"].(map[string]any) + if !ok { + return nil, fmt.Errorf("OpenAPI document has no paths object") + } + usedTags := map[string]bool{} + for path, rawItem := range paths { + item, ok := rawItem.(map[string]any) + if !ok { + return nil, fmt.Errorf("path %q is not an object", path) + } + for method, rawOperation := range item { + if !httpMethods[strings.ToLower(method)] { + continue + } + operation, ok := rawOperation.(map[string]any) + if !ok { + return nil, fmt.Errorf("operation %s %s is not an object", method, path) + } + operationID, _ := operation["operationId"].(string) + visibility, err := operationVisibility(operationID) + if err != nil { + return nil, fmt.Errorf("operation %s %s: %w", method, path, err) + } + if visibility != optionsv1.ApiVisibility_API_VISIBILITY_PUBLIC { + delete(item, method) + continue + } + if tags, ok := operation["tags"].([]any); ok { + for _, rawTag := range tags { + if tag, valid := rawTag.(string); valid { + usedTags[tag] = true + } + } + } + } + if !containsOperation(item) { + delete(paths, path) + } + } + + filterTags(document, usedTags) + filterOAuthScopes(document) + if err := pruneSchemas(document); err != nil { + return nil, err + } + + output, err := json.MarshalIndent(document, "", " ") + if err != nil { + return nil, fmt.Errorf("encode public OpenAPI document: %w", err) + } + return append(output, '\n'), nil +} + +func filterOAuthScopes(document map[string]any) { + used := map[string]bool{} + paths, _ := document["paths"].(map[string]any) + for _, rawItem := range paths { + item, _ := rawItem.(map[string]any) + for method, rawOperation := range item { + if !httpMethods[strings.ToLower(method)] { + continue + } + operation, _ := rawOperation.(map[string]any) + security, _ := operation["security"].([]any) + for _, rawRequirement := range security { + requirement, _ := rawRequirement.(map[string]any) + for _, rawScopes := range requirement { + scopes, _ := rawScopes.([]any) + for _, rawScope := range scopes { + if scope, ok := rawScope.(string); ok { + used[scope] = true + } + } + } + } + } + } + + components, _ := document["components"].(map[string]any) + securitySchemes, _ := components["securitySchemes"].(map[string]any) + for _, rawScheme := range securitySchemes { + scheme, _ := rawScheme.(map[string]any) + if scheme["type"] != "oauth2" { + continue + } + flows, _ := scheme["flows"].(map[string]any) + for _, rawFlow := range flows { + flow, _ := rawFlow.(map[string]any) + scopes, _ := flow["scopes"].(map[string]any) + for scope := range scopes { + if !used[scope] { + delete(scopes, scope) + } + } + } + } +} + +func operationVisibility(operationID string) (optionsv1.ApiVisibility, error) { + operationID = strings.TrimSuffix(operationID, ".get") + separator := strings.LastIndexByte(operationID, '.') + if separator <= 0 || separator == len(operationID)-1 { + return optionsv1.ApiVisibility_API_VISIBILITY_UNSPECIFIED, fmt.Errorf("invalid operationId %q", operationID) + } + serviceName := protoreflect.FullName(operationID[:separator]) + methodName := protoreflect.Name(operationID[separator+1:]) + descriptor, err := protoregistry.GlobalFiles.FindDescriptorByName(serviceName) + if err != nil { + return optionsv1.ApiVisibility_API_VISIBILITY_UNSPECIFIED, fmt.Errorf("find service %q: %w", serviceName, err) + } + service, ok := descriptor.(protoreflect.ServiceDescriptor) + if !ok { + return optionsv1.ApiVisibility_API_VISIBILITY_UNSPECIFIED, fmt.Errorf("%q is not a service", serviceName) + } + serviceVisibility := getServiceVisibility(service) + if serviceVisibility == optionsv1.ApiVisibility_API_VISIBILITY_UNSPECIFIED { + return serviceVisibility, fmt.Errorf("service %q has no explicit API visibility", serviceName) + } + method := service.Methods().ByName(methodName) + if method == nil { + return optionsv1.ApiVisibility_API_VISIBILITY_UNSPECIFIED, fmt.Errorf("service %q has no method %q", serviceName, methodName) + } + methodOptions, _ := method.Options().(*descriptorpb.MethodOptions) + if methodOptions == nil || !proto.HasExtension(methodOptions, optionsv1.E_MethodApiVisibility) { + return serviceVisibility, nil + } + methodVisibility, ok := proto.GetExtension(methodOptions, optionsv1.E_MethodApiVisibility).(optionsv1.ApiVisibility) + if !ok || methodVisibility == optionsv1.ApiVisibility_API_VISIBILITY_UNSPECIFIED { + return optionsv1.ApiVisibility_API_VISIBILITY_UNSPECIFIED, fmt.Errorf("method %q has an invalid API visibility", method.FullName()) + } + if serviceVisibility == optionsv1.ApiVisibility_API_VISIBILITY_INTERNAL && methodVisibility == optionsv1.ApiVisibility_API_VISIBILITY_PUBLIC { + return optionsv1.ApiVisibility_API_VISIBILITY_UNSPECIFIED, fmt.Errorf("method %q cannot widen an internal service to public", method.FullName()) + } + return methodVisibility, nil +} + +func getServiceVisibility(service protoreflect.ServiceDescriptor) optionsv1.ApiVisibility { + serviceOptions, _ := service.Options().(*descriptorpb.ServiceOptions) + if serviceOptions == nil || !proto.HasExtension(serviceOptions, optionsv1.E_ServiceApiVisibility) { + return optionsv1.ApiVisibility_API_VISIBILITY_UNSPECIFIED + } + visibility, ok := proto.GetExtension(serviceOptions, optionsv1.E_ServiceApiVisibility).(optionsv1.ApiVisibility) + if !ok { + return optionsv1.ApiVisibility_API_VISIBILITY_UNSPECIFIED + } + return visibility +} + +func containsOperation(item map[string]any) bool { + for key := range item { + if httpMethods[strings.ToLower(key)] { + return true + } + } + return false +} + +func filterTags(document map[string]any, used map[string]bool) { + tags, ok := document["tags"].([]any) + if !ok { + return + } + filtered := make([]any, 0, len(tags)) + for _, rawTag := range tags { + tag, ok := rawTag.(map[string]any) + if !ok { + continue + } + name, _ := tag["name"].(string) + if used[name] { + filtered = append(filtered, tag) + } + } + document["tags"] = filtered +} + +func pruneSchemas(document map[string]any) error { + components, ok := document["components"].(map[string]any) + if !ok { + return nil + } + schemas, ok := components["schemas"].(map[string]any) + if !ok { + return nil + } + + needed := map[string]bool{} + collectSchemaRefs(document["paths"], needed) + queue := make([]string, 0, len(needed)) + for name := range needed { + queue = append(queue, name) + } + for len(queue) > 0 { + name := queue[0] + queue = queue[1:] + schema, exists := schemas[name] + if !exists { + return fmt.Errorf("public OpenAPI document references missing schema %q", name) + } + found := map[string]bool{} + collectSchemaRefs(schema, found) + for nested := range found { + if !needed[nested] { + needed[nested] = true + queue = append(queue, nested) + } + } + } + + retained := make(map[string]any, len(needed)) + names := make([]string, 0, len(needed)) + for name := range needed { + names = append(names, name) + } + sort.Strings(names) + for _, name := range names { + retained[name] = schemas[name] + } + components["schemas"] = retained + return nil +} + +func collectSchemaRefs(value any, found map[string]bool) { + switch typed := value.(type) { + case map[string]any: + for key, nested := range typed { + if key == "$ref" { + if ref, ok := nested.(string); ok { + const prefix = "#/components/schemas/" + if strings.HasPrefix(ref, prefix) { + name := strings.TrimPrefix(ref, prefix) + name = strings.ReplaceAll(strings.ReplaceAll(name, "~1", "/"), "~0", "~") + found[name] = true + } + } + continue + } + collectSchemaRefs(nested, found) + } + case []any: + for _, nested := range typed { + collectSchemaRefs(nested, found) + } + } +} diff --git a/internal/openapivisibility/filter_test.go b/internal/openapivisibility/filter_test.go new file mode 100644 index 0000000..f461650 --- /dev/null +++ b/internal/openapivisibility/filter_test.go @@ -0,0 +1,73 @@ +package openapivisibility + +import ( + "encoding/json" + "testing" +) + +func TestFilterRemovesInternalOperationsAndUnreachableSchemas(t *testing.T) { + t.Parallel() + + input := []byte(`{ + "openapi": "3.1.0", + "paths": { + "/task/create": {"post": {"operationId": "libops.v1.TaskService.CreateTask", "tags": ["libops.v1.TaskService"], "security": [{"oauth2": ["write:organization"]}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/PublicRequest"}}}}}}, + "/task/log": {"post": {"operationId": "libops.v1.TaskService.AppendTaskLog", "tags": ["libops.v1.TaskService"]}}, + "/admin/account": {"get": {"operationId": "libops.v1.AdminAccountService.GetAccount.get", "tags": ["libops.v1.AdminAccountService"]}} + }, + "tags": [{"name": "libops.v1.TaskService"}, {"name": "libops.v1.AdminAccountService"}], + "components": {"schemas": { + "PublicRequest": {"properties": {"nested": {"$ref": "#/components/schemas/PublicNested"}}}, + "PublicNested": {"type": "object"}, + "AdminSecret": {"type": "object"} + }, "securitySchemes": {"oauth2": {"type": "oauth2", "flows": {"authorizationCode": {"scopes": {"write:organization": "Write", "admin:system": "Admin"}}}}}} +}`) + + output, err := Filter(input) + if err != nil { + t.Fatalf("Filter() error = %v", err) + } + var got map[string]any + if err := json.Unmarshal(output, &got); err != nil { + t.Fatalf("decode output: %v", err) + } + paths := got["paths"].(map[string]any) + if _, exists := paths["/task/create"]; !exists { + t.Error("public operation was removed") + } + if _, exists := paths["/task/log"]; exists { + t.Error("internal method on public service was retained") + } + if _, exists := paths["/admin/account"]; exists { + t.Error("internal service operation was retained") + } + schemas := got["components"].(map[string]any)["schemas"].(map[string]any) + if _, exists := schemas["PublicNested"]; !exists { + t.Error("transitively referenced public schema was removed") + } + if _, exists := schemas["AdminSecret"]; exists { + t.Error("unreachable internal schema was retained") + } + tags := got["tags"].([]any) + if len(tags) != 1 { + t.Errorf("retained %d tags; want 1", len(tags)) + } + securitySchemes := got["components"].(map[string]any)["securitySchemes"].(map[string]any) + flows := securitySchemes["oauth2"].(map[string]any)["flows"].(map[string]any) + scopes := flows["authorizationCode"].(map[string]any)["scopes"].(map[string]any) + if _, exists := scopes["write:organization"]; !exists { + t.Error("OAuth scope used by public operation was removed") + } + if _, exists := scopes["admin:system"]; exists { + t.Error("internal-only OAuth scope was retained") + } +} + +func TestFilterFailsClosedForUnknownOperation(t *testing.T) { + t.Parallel() + + _, err := Filter([]byte(`{"paths":{"/new":{"post":{"operationId":"libops.v1.MissingService.New"}}}}`)) + if err == nil { + t.Fatal("Filter() error = nil; want unknown descriptor error") + } +} diff --git a/libops/v1/admin/project.pb.go b/libops/v1/admin/project.pb.go index 48ab9d0..4f9c4db 100644 --- a/libops/v1/admin/project.pb.go +++ b/libops/v1/admin/project.pb.go @@ -10,7 +10,6 @@ import ( common "github.com/libops/proto/libops/v1/common" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - _ "google.golang.org/protobuf/types/known/fieldmaskpb" reflect "reflect" sync "sync" unsafe "unsafe" @@ -115,7 +114,7 @@ var File_libops_v1_admin_project_proto protoreflect.FileDescriptor const file_libops_v1_admin_project_proto_rawDesc = "" + "\n" + - "\x1dlibops/v1/admin/project.proto\x12\x0flibops.v1.admin\x1a\x1elibops/v1/common/project.proto\x1a google/protobuf/field_mask.proto\"\xfb\x02\n" + + "\x1dlibops/v1/admin/project.proto\x12\x0flibops.v1.admin\x1a\x1elibops/v1/common/project.proto\"\xfb\x02\n" + "\x12AdminProjectConfig\x127\n" + "\x06config\x18\x01 \x01(\v2\x1f.libops.v1.common.ProjectConfigR\x06config\x12'\n" + "\x0fbilling_account\x18\x02 \x01(\tR\x0ebillingAccount\x121\n" + diff --git a/libops/v1/admin/project.proto b/libops/v1/admin/project.proto index 5c23b38..52e2dd1 100644 --- a/libops/v1/admin/project.proto +++ b/libops/v1/admin/project.proto @@ -3,7 +3,6 @@ syntax = "proto3"; package libops.v1.admin; import "libops/v1/common/project.proto"; -import "google/protobuf/field_mask.proto"; option go_package = "github.com/libops/proto/libops/v1/admin;adminv1"; diff --git a/libops/v1/admin_account_api.pb.go b/libops/v1/admin_account_api.pb.go index 0ea2d8a..9037ef7 100644 --- a/libops/v1/admin_account_api.pb.go +++ b/libops/v1/admin_account_api.pb.go @@ -11,7 +11,6 @@ import ( _ "github.com/libops/proto/libops/v1/options" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - _ "google.golang.org/protobuf/types/descriptorpb" emptypb "google.golang.org/protobuf/types/known/emptypb" fieldmaskpb "google.golang.org/protobuf/types/known/fieldmaskpb" reflect "reflect" @@ -1079,7 +1078,7 @@ var File_libops_v1_admin_account_api_proto protoreflect.FileDescriptor const file_libops_v1_admin_account_api_proto_rawDesc = "" + "\n" + - "!libops/v1/admin_account_api.proto\x12\tlibops.v1\x1a google/protobuf/descriptor.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\x1a\x1dlibops/v1/options/scope.proto\x1a\x1clibops/v1/common/types.proto\"\x83\x03\n" + + "!libops/v1/admin_account_api.proto\x12\tlibops.v1\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\x1a\x1dlibops/v1/options/scope.proto\x1a\"libops/v1/options/visibility.proto\x1a\x1clibops/v1/common/types.proto\"\x83\x03\n" + "\aAccount\x12\x1d\n" + "\n" + "account_id\x18\x01 \x01(\tR\taccountId\x12\x14\n" + @@ -1167,7 +1166,7 @@ const file_libops_v1_admin_account_api_proto_rawDesc = "" + "\x1aACCOUNT_STATUS_UNSPECIFIED\x10\x00\x12\x19\n" + "\x15ACCOUNT_STATUS_ACTIVE\x10\x01\x12\x1c\n" + "\x18ACCOUNT_STATUS_SUSPENDED\x10\x02\x12\x1a\n" + - "\x16ACCOUNT_STATUS_DELETED\x10\x032\xb6\a\n" + + "\x16ACCOUNT_STATUS_DELETED\x10\x032\xbc\a\n" + "\x13AdminAccountService\x12d\n" + "\n" + "GetAccount\x12\x1c.libops.v1.GetAccountRequest\x1a\x1d.libops.v1.GetAccountResponse\"\x19\x92\xb5\x18\x12\b\x01\x10\x03\"\fadmin:system\x90\x02\x01\x12\x83\x01\n" + @@ -1177,7 +1176,7 @@ const file_libops_v1_admin_account_api_proto_rawDesc = "" + "\rDeleteAccount\x12\x1f.libops.v1.DeleteAccountRequest\x1a\x16.google.protobuf.Empty\"\x16\x92\xb5\x18\x12\b\x01\x10\x03\"\fadmin:system\x12j\n" + "\fListAccounts\x12\x1e.libops.v1.ListAccountsRequest\x1a\x1f.libops.v1.ListAccountsResponse\"\x19\x92\xb5\x18\x12\b\x01\x10\x03\"\fadmin:system\x90\x02\x01\x12\x7f\n" + "\x13ListAccountProjects\x12%.libops.v1.ListAccountProjectsRequest\x1a&.libops.v1.ListAccountProjectsResponse\"\x19\x92\xb5\x18\x12\b\x01\x10\x03\"\fadmin:system\x90\x02\x01\x12\x8b\x01\n" + - "\x17ListAccountRepositories\x12).libops.v1.ListAccountRepositoriesRequest\x1a*.libops.v1.ListAccountRepositoriesResponse\"\x19\x92\xb5\x18\x12\b\x01\x10\x03\"\fadmin:system\x90\x02\x01B\x96\x01\n" + + "\x17ListAccountRepositories\x12).libops.v1.ListAccountRepositoriesRequest\x1a*.libops.v1.ListAccountRepositoriesResponse\"\x19\x92\xb5\x18\x12\b\x01\x10\x03\"\fadmin:system\x90\x02\x01\x1a\x04\xa0\xb5\x18\x02B\x96\x01\n" + "\rcom.libops.v1B\x14AdminAccountApiProtoP\x01Z*github.com/libops/proto/libops/v1;libopsv1\xa2\x02\x03LXX\xaa\x02\tLibops.V1\xca\x02\tLibops\\V1\xe2\x02\x15Libops\\V1\\GPBMetadata\xea\x02\n" + "Libops::V1b\x06proto3" diff --git a/libops/v1/admin_account_api.proto b/libops/v1/admin_account_api.proto index 446e666..bcf0c97 100644 --- a/libops/v1/admin_account_api.proto +++ b/libops/v1/admin_account_api.proto @@ -2,16 +2,18 @@ syntax = "proto3"; package libops.v1; -import "google/protobuf/descriptor.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/field_mask.proto"; import "libops/v1/options/scope.proto"; +import "libops/v1/options/visibility.proto"; import "libops/v1/common/types.proto"; option go_package = "github.com/libops/proto/libops/v1;libopsv1"; // AdminAccountService manages user accounts (admin only) service AdminAccountService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_INTERNAL; + // Get account information by ID rpc GetAccount(GetAccountRequest) returns (GetAccountResponse) { option idempotency_level = NO_SIDE_EFFECTS; diff --git a/libops/v1/admin_api.pb.go b/libops/v1/admin_api.pb.go index 8aefb58..975378e 100644 --- a/libops/v1/admin_api.pb.go +++ b/libops/v1/admin_api.pb.go @@ -11,7 +11,6 @@ import ( _ "github.com/libops/proto/libops/v1/options" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - _ "google.golang.org/protobuf/types/descriptorpb" emptypb "google.golang.org/protobuf/types/known/emptypb" fieldmaskpb "google.golang.org/protobuf/types/known/fieldmaskpb" reflect "reflect" @@ -3250,7 +3249,7 @@ var File_libops_v1_admin_api_proto protoreflect.FileDescriptor const file_libops_v1_admin_api_proto_rawDesc = "" + "\n" + - "\x19libops/v1/admin_api.proto\x12\tlibops.v1\x1a google/protobuf/descriptor.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\x1a\x1dlibops/v1/options/scope.proto\x1a\x1dlibops/v1/admin/project.proto\x1a\"libops/v1/admin/organization.proto\x1a\x1alibops/v1/admin/site.proto\"`\n" + + "\x19libops/v1/admin_api.proto\x12\tlibops.v1\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\x1a\x1dlibops/v1/options/scope.proto\x1a\"libops/v1/options/visibility.proto\x1a\x1dlibops/v1/admin/project.proto\x1a\"libops/v1/admin/organization.proto\x1a\x1alibops/v1/admin/site.proto\"`\n" + "\x16AdminGetProjectRequest\x12'\n" + "\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x1d\n" + "\n" + @@ -3495,14 +3494,14 @@ const file_libops_v1_admin_api_proto_rawDesc = "" + "%CONVERGENCE_RESOURCE_TYPE_UNSPECIFIED\x10\x00\x12*\n" + "&CONVERGENCE_RESOURCE_TYPE_ORGANIZATION\x10\x01\x12%\n" + "!CONVERGENCE_RESOURCE_TYPE_PROJECT\x10\x02\x12\"\n" + - "\x1eCONVERGENCE_RESOURCE_TYPE_SITE\x10\x032\xb7\x06\n" + + "\x1eCONVERGENCE_RESOURCE_TYPE_SITE\x10\x032\xbd\x06\n" + "\x18AdminOrganizationService\x12}\n" + "\x0fGetOrganization\x12&.libops.v1.AdminGetOrganizationRequest\x1a'.libops.v1.AdminGetOrganizationResponse\"\x19\x92\xb5\x18\x12\b\x01\x10\x03\"\fadmin:system\x90\x02\x01\x12\x83\x01\n" + "\x12CreateOrganization\x12).libops.v1.AdminCreateOrganizationRequest\x1a*.libops.v1.AdminCreateOrganizationResponse\"\x16\x92\xb5\x18\x12\b\x01\x10\x03\"\fadmin:system\x12\x83\x01\n" + "\x12UpdateOrganization\x12).libops.v1.AdminUpdateOrganizationRequest\x1a*.libops.v1.AdminUpdateOrganizationResponse\"\x16\x92\xb5\x18\x12\b\x01\x10\x03\"\fadmin:system\x12o\n" + "\x12DeleteOrganization\x12).libops.v1.AdminDeleteOrganizationRequest\x1a\x16.google.protobuf.Empty\"\x16\x92\xb5\x18\x12\b\x01\x10\x03\"\fadmin:system\x12\x83\x01\n" + "\x11ListOrganizations\x12(.libops.v1.AdminListOrganizationsRequest\x1a).libops.v1.AdminListOrganizationsResponse\"\x19\x92\xb5\x18\x12\b\x01\x10\x03\"\fadmin:system\x90\x02\x01\x12\x98\x01\n" + - "\x18ListOrganizationProjects\x12/.libops.v1.AdminListOrganizationProjectsRequest\x1a0.libops.v1.AdminListOrganizationProjectsResponse\"\x19\x92\xb5\x18\x12\b\x01\x10\x03\"\fadmin:system\x90\x02\x012\x9b\t\n" + + "\x18ListOrganizationProjects\x12/.libops.v1.AdminListOrganizationProjectsRequest\x1a0.libops.v1.AdminListOrganizationProjectsResponse\"\x19\x92\xb5\x18\x12\b\x01\x10\x03\"\fadmin:system\x90\x02\x01\x1a\x04\xa0\xb5\x18\x022\xa1\t\n" + "\x10AdminSiteService\x12k\n" + "\tListSites\x12 .libops.v1.AdminListSitesRequest\x1a!.libops.v1.AdminListSitesResponse\"\x19\x92\xb5\x18\x12\b\x01\x10\x03\"\fadmin:system\x90\x02\x01\x12e\n" + "\aGetSite\x12\x1e.libops.v1.AdminGetSiteRequest\x1a\x1f.libops.v1.AdminGetSiteResponse\"\x19\x92\xb5\x18\x12\b\x01\x10\x03\"\fadmin:system\x90\x02\x01\x12k\n" + @@ -3518,7 +3517,7 @@ const file_libops_v1_admin_api_proto_rawDesc = "" + "\x0fGetSiteFirewall\x12!.libops.v1.GetSiteFirewallRequest\x1a\".libops.v1.GetSiteFirewallResponse\"\x03\x90\x02\x01\x12N\n" + "\vSiteCheckIn\x12\x1d.libops.v1.SiteCheckInRequest\x1a\x1e.libops.v1.SiteCheckInResponse\"\x00\x12T\n" + "\fSyncManifest\x12\x1e.libops.v1.SyncManifestRequest\x1a\x1f.libops.v1.SyncManifestResponse\"\x03\x90\x02\x01\x12E\n" + - "\aGetBlob\x12\x19.libops.v1.GetBlobRequest\x1a\x1a.libops.v1.GetBlobResponse\"\x03\x90\x02\x012\xcd\x05\n" + + "\aGetBlob\x12\x19.libops.v1.GetBlobRequest\x1a\x1a.libops.v1.GetBlobResponse\"\x03\x90\x02\x01\x1a\x04\xa0\xb5\x18\x022\xd3\x05\n" + "\x13AdminProjectService\x12n\n" + "\n" + "GetProject\x12!.libops.v1.AdminGetProjectRequest\x1a\".libops.v1.AdminGetProjectResponse\"\x19\x92\xb5\x18\x12\b\x01\x10\x03\"\fadmin:system\x90\x02\x01\x12t\n" + @@ -3526,13 +3525,13 @@ const file_libops_v1_admin_api_proto_rawDesc = "" + "\rUpdateProject\x12$.libops.v1.AdminUpdateProjectRequest\x1a%.libops.v1.AdminUpdateProjectResponse\"\x16\x92\xb5\x18\x12\b\x01\x10\x03\"\fadmin:system\x12e\n" + "\rDeleteProject\x12$.libops.v1.AdminDeleteProjectRequest\x1a\x16.google.protobuf.Empty\"\x16\x92\xb5\x18\x12\b\x01\x10\x03\"\fadmin:system\x12t\n" + "\fListProjects\x12#.libops.v1.AdminListProjectsRequest\x1a$.libops.v1.AdminListProjectsResponse\"\x19\x92\xb5\x18\x12\b\x01\x10\x03\"\fadmin:system\x90\x02\x01\x12}\n" + - "\x0fListAllProjects\x12&.libops.v1.AdminListAllProjectsRequest\x1a'.libops.v1.AdminListAllProjectsResponse\"\x19\x92\xb5\x18\x12\b\x01\x10\x03\"\fadmin:system\x90\x02\x012\xf8\x02\n" + + "\x0fListAllProjects\x12&.libops.v1.AdminListAllProjectsRequest\x1a'.libops.v1.AdminListAllProjectsResponse\"\x19\x92\xb5\x18\x12\b\x01\x10\x03\"\fadmin:system\x90\x02\x01\x1a\x04\xa0\xb5\x18\x022\xfe\x02\n" + "\x1aAdminReconciliationService\x12l\n" + "\x14GetReconciliationRun\x12&.libops.v1.GetReconciliationRunRequest\x1a'.libops.v1.GetReconciliationRunResponse\"\x03\x90\x02\x01\x12{\n" + "\x1aUpdateReconciliationStatus\x12,.libops.v1.UpdateReconciliationStatusRequest\x1a-.libops.v1.UpdateReconciliationStatusResponse\"\x00\x12o\n" + - "\x15GenerateTerraformVars\x12'.libops.v1.GenerateTerraformVarsRequest\x1a(.libops.v1.GenerateTerraformVarsResponse\"\x03\x90\x02\x012\x8e\x01\n" + + "\x15GenerateTerraformVars\x12'.libops.v1.GenerateTerraformVarsRequest\x1a(.libops.v1.GenerateTerraformVarsResponse\"\x03\x90\x02\x01\x1a\x04\xa0\xb5\x18\x022\x94\x01\n" + "\x17AdminConvergenceService\x12s\n" + - "\x10CheckConvergence\x12\".libops.v1.CheckConvergenceRequest\x1a#.libops.v1.CheckConvergenceResponse\"\x16\x92\xb5\x18\x12\b\x01\x10\x03\"\fadmin:systemB\x8f\x01\n" + + "\x10CheckConvergence\x12\".libops.v1.CheckConvergenceRequest\x1a#.libops.v1.CheckConvergenceResponse\"\x16\x92\xb5\x18\x12\b\x01\x10\x03\"\fadmin:system\x1a\x04\xa0\xb5\x18\x02B\x8f\x01\n" + "\rcom.libops.v1B\rAdminApiProtoP\x01Z*github.com/libops/proto/libops/v1;libopsv1\xa2\x02\x03LXX\xaa\x02\tLibops.V1\xca\x02\tLibops\\V1\xe2\x02\x15Libops\\V1\\GPBMetadata\xea\x02\n" + "Libops::V1b\x06proto3" diff --git a/libops/v1/admin_api.proto b/libops/v1/admin_api.proto index d8ed8e3..0d8e239 100644 --- a/libops/v1/admin_api.proto +++ b/libops/v1/admin_api.proto @@ -2,10 +2,10 @@ syntax = "proto3"; package libops.v1; -import "google/protobuf/descriptor.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/field_mask.proto"; import "libops/v1/options/scope.proto"; +import "libops/v1/options/visibility.proto"; import "libops/v1/admin/project.proto"; import "libops/v1/admin/organization.proto"; import "libops/v1/admin/site.proto"; @@ -14,6 +14,8 @@ option go_package = "github.com/libops/proto/libops/v1;libopsv1"; // AdminOrganizationService manages admin-level organization operations with full access service AdminOrganizationService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_INTERNAL; + // Get organization configuration (admin view - includes sensitive fields) rpc GetOrganization(AdminGetOrganizationRequest) returns (AdminGetOrganizationResponse) { option idempotency_level = NO_SIDE_EFFECTS; @@ -50,6 +52,8 @@ service AdminOrganizationService { // AdminSiteService manages admin-level site operations with full access service AdminSiteService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_INTERNAL; + // List sites (admin view) rpc ListSites(AdminListSitesRequest) returns (AdminListSitesResponse) { option idempotency_level = NO_SIDE_EFFECTS; @@ -116,6 +120,8 @@ service AdminSiteService { // AdminProjectService manages admin-level project operations with full access service AdminProjectService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_INTERNAL; + // Get project configuration (admin view - includes sensitive fields) rpc GetProject(AdminGetProjectRequest) returns (AdminGetProjectResponse) { option idempotency_level = NO_SIDE_EFFECTS; @@ -153,6 +159,8 @@ service AdminProjectService { // AdminReconciliationService handles reconciliation operations // Called by Cloud Run reconciliation services with GSA authentication service AdminReconciliationService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_INTERNAL; + // Get reconciliation run details from control-plane database rpc GetReconciliationRun(GetReconciliationRunRequest) returns (GetReconciliationRunResponse) { option idempotency_level = NO_SIDE_EFFECTS; @@ -171,6 +179,8 @@ service AdminReconciliationService { // AdminConvergenceService checks desired-state convergence and can request // repair workflows for stuck resources. service AdminConvergenceService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_INTERNAL; + rpc CheckConvergence(CheckConvergenceRequest) returns (CheckConvergenceResponse) { option (libops.v1.options.required_scope) = { resource: RESOURCE_TYPE_SYSTEM, level: ACCESS_LEVEL_ADMIN, oauth_scopes: "admin:system" }; } diff --git a/libops/v1/assistant_api.pb.go b/libops/v1/assistant_api.pb.go index 122d71f..07cce3b 100644 --- a/libops/v1/assistant_api.pb.go +++ b/libops/v1/assistant_api.pb.go @@ -199,7 +199,7 @@ var File_libops_v1_assistant_api_proto protoreflect.FileDescriptor const file_libops_v1_assistant_api_proto_rawDesc = "" + "\n" + - "\x1dlibops/v1/assistant_api.proto\x12\tlibops.v1\x1a\x1dlibops/v1/options/scope.proto\x1a\x18libops/v1/task_api.proto\"\x98\x03\n" + + "\x1dlibops/v1/assistant_api.proto\x12\tlibops.v1\x1a\x1dlibops/v1/options/scope.proto\x1a\"libops/v1/options/visibility.proto\x1a\x18libops/v1/task_api.proto\"\x98\x03\n" + "\x14AssistantChatRequest\x12'\n" + "\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x1d\n" + "\n" + @@ -218,9 +218,9 @@ const file_libops_v1_assistant_api_proto_rawDesc = "" + "\n" + "request_id\x18\x01 \x01(\tR\trequestId\x12\x16\n" + "\x06status\x18\x02 \x01(\tR\x06status\x12\x14\n" + - "\x05reply\x18\x03 \x01(\tR\x05reply2\x8e\x01\n" + + "\x05reply\x18\x03 \x01(\tR\x05reply2\x94\x01\n" + "\x10AssistantService\x12z\n" + - "\x04Chat\x12\x1f.libops.v1.AssistantChatRequest\x1a .libops.v1.AssistantChatResponse\"/\x92\xb5\x18+\b\x03\x10\x02\x18\x01\"\x12write:organization*\x0forganization_idB\x93\x01\n" + + "\x04Chat\x12\x1f.libops.v1.AssistantChatRequest\x1a .libops.v1.AssistantChatResponse\"/\x92\xb5\x18+\b\x03\x10\x02\x18\x01\"\x12write:organization*\x0forganization_id\x1a\x04\xa0\xb5\x18\x01B\x93\x01\n" + "\rcom.libops.v1B\x11AssistantApiProtoP\x01Z*github.com/libops/proto/libops/v1;libopsv1\xa2\x02\x03LXX\xaa\x02\tLibops.V1\xca\x02\tLibops\\V1\xe2\x02\x15Libops\\V1\\GPBMetadata\xea\x02\n" + "Libops::V1b\x06proto3" diff --git a/libops/v1/assistant_api.proto b/libops/v1/assistant_api.proto index ba023a6..df40a45 100644 --- a/libops/v1/assistant_api.proto +++ b/libops/v1/assistant_api.proto @@ -3,12 +3,15 @@ syntax = "proto3"; package libops.v1; import "libops/v1/options/scope.proto"; +import "libops/v1/options/visibility.proto"; import "libops/v1/task_api.proto"; option go_package = "github.com/libops/proto/libops/v1;libopsv1"; // AssistantService manages chat-driven automation requests. service AssistantService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_PUBLIC; + // Chat sends a user request to the assistant automation system. rpc Chat(AssistantChatRequest) returns (AssistantChatResponse) { option (libops.v1.options.required_scope) = { diff --git a/libops/v1/operation_api.pb.go b/libops/v1/operation_api.pb.go index 2ca6f25..5d655a6 100644 --- a/libops/v1/operation_api.pb.go +++ b/libops/v1/operation_api.pb.go @@ -7,6 +7,7 @@ package libopsv1 import ( + _ "github.com/libops/proto/libops/v1/options" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" timestamppb "google.golang.org/protobuf/types/known/timestamppb" @@ -341,7 +342,7 @@ var File_libops_v1_operation_api_proto protoreflect.FileDescriptor const file_libops_v1_operation_api_proto_rawDesc = "" + "\n" + - "\x1dlibops/v1/operation_api.proto\x12\tlibops.v1\x1a\x1fgoogle/protobuf/timestamp.proto\"\xcd\x06\n" + + "\x1dlibops/v1/operation_api.proto\x12\tlibops.v1\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\"libops/v1/options/visibility.proto\"\xcd\x06\n" + "\tOperation\x12!\n" + "\foperation_id\x18\x01 \x01(\tR\voperationId\x12\x1d\n" + "\n" + @@ -374,9 +375,9 @@ const file_libops_v1_operation_api_proto_rawDesc = "" + "\x18OPERATION_STATUS_RUNNING\x10\x01\x12\x1e\n" + "\x1aOPERATION_STATUS_SUCCEEDED\x10\x02\x12\x1b\n" + "\x17OPERATION_STATUS_FAILED\x10\x03\x12\x1d\n" + - "\x19OPERATION_STATUS_CANCELED\x10\x042h\n" + + "\x19OPERATION_STATUS_CANCELED\x10\x042n\n" + "\x10OperationService\x12T\n" + - "\fGetOperation\x12\x1e.libops.v1.GetOperationRequest\x1a\x1f.libops.v1.GetOperationResponse\"\x03\x90\x02\x01B\x93\x01\n" + + "\fGetOperation\x12\x1e.libops.v1.GetOperationRequest\x1a\x1f.libops.v1.GetOperationResponse\"\x03\x90\x02\x01\x1a\x04\xa0\xb5\x18\x01B\x93\x01\n" + "\rcom.libops.v1B\x11OperationApiProtoP\x01Z*github.com/libops/proto/libops/v1;libopsv1\xa2\x02\x03LXX\xaa\x02\tLibops.V1\xca\x02\tLibops\\V1\xe2\x02\x15Libops\\V1\\GPBMetadata\xea\x02\n" + "Libops::V1b\x06proto3" diff --git a/libops/v1/operation_api.proto b/libops/v1/operation_api.proto index 0aa583c..30fe79f 100644 --- a/libops/v1/operation_api.proto +++ b/libops/v1/operation_api.proto @@ -3,11 +3,14 @@ syntax = "proto3"; package libops.v1; import "google/protobuf/timestamp.proto"; +import "libops/v1/options/visibility.proto"; option go_package = "github.com/libops/proto/libops/v1;libopsv1"; // OperationService exposes end-to-end status for customer-visible mutations. service OperationService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_PUBLIC; + // Get an operation by public operation ID or request ID. rpc GetOperation(GetOperationRequest) returns (GetOperationResponse) { option idempotency_level = NO_SIDE_EFFECTS; diff --git a/libops/v1/options/contracts_test.go b/libops/v1/options/contracts_test.go new file mode 100644 index 0000000..35b009e --- /dev/null +++ b/libops/v1/options/contracts_test.go @@ -0,0 +1,157 @@ +package options_test + +import ( + "strings" + "testing" + + _ "github.com/libops/proto/libops/v1" + optionsv1 "github.com/libops/proto/libops/v1/options" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" + "google.golang.org/protobuf/types/descriptorpb" +) + +func TestEveryLibOpsServiceDeclaresAPIVisibility(t *testing.T) { + t.Parallel() + + serviceCount := 0 + protoregistry.GlobalFiles.RangeFiles(func(file protoreflect.FileDescriptor) bool { + if !strings.HasPrefix(file.Path(), "libops/v1/") { + return true + } + services := file.Services() + for i := 0; i < services.Len(); i++ { + serviceCount++ + service := services.Get(i) + declaredVisibility := serviceVisibility(service) + if declaredVisibility == optionsv1.ApiVisibility_API_VISIBILITY_UNSPECIFIED { + t.Errorf("service %s must declare service_api_visibility", service.FullName()) + } + methods := service.Methods() + for j := 0; j < methods.Len(); j++ { + method := methods.Get(j) + visibility, explicit := explicitMethodVisibility(method) + if !explicit { + continue + } + if visibility == optionsv1.ApiVisibility_API_VISIBILITY_UNSPECIFIED { + t.Errorf("method %s has an unspecified method_api_visibility override", method.FullName()) + } + if declaredVisibility == optionsv1.ApiVisibility_API_VISIBILITY_INTERNAL && visibility == optionsv1.ApiVisibility_API_VISIBILITY_PUBLIC { + t.Errorf("method %s cannot widen an internal service to public", method.FullName()) + } + } + } + return true + }) + + if serviceCount != 28 { + t.Fatalf("checked %d services; want 28 (update the contract count when adding a service)", serviceCount) + } +} + +func TestInternalAPIContract(t *testing.T) { + t.Parallel() + + tests := map[protoreflect.FullName]optionsv1.ApiVisibility{ + "libops.v1.AdminAccountService.GetAccount": optionsv1.ApiVisibility_API_VISIBILITY_INTERNAL, + "libops.v1.TaskService.AppendTaskLog": optionsv1.ApiVisibility_API_VISIBILITY_INTERNAL, + "libops.v1.TaskService.CreateTask": optionsv1.ApiVisibility_API_VISIBILITY_PUBLIC, + } + for name, want := range tests { + descriptor, err := protoregistry.GlobalFiles.FindDescriptorByName(name) + if err != nil { + t.Fatalf("find %s: %v", name, err) + } + method, ok := descriptor.(protoreflect.MethodDescriptor) + if !ok { + t.Fatalf("%s is %T, want method descriptor", name, descriptor) + } + if got := methodVisibility(method); got != want { + t.Errorf("%s visibility = %s; want %s", name, got, want) + } + } +} + +func TestAssistantPlaygroundMethodsCannotAcceptSensitiveFields(t *testing.T) { + t.Parallel() + + protoregistry.GlobalFiles.RangeFiles(func(file protoreflect.FileDescriptor) bool { + if !strings.HasPrefix(file.Path(), "libops/v1/") { + return true + } + services := file.Services() + for i := 0; i < services.Len(); i++ { + methods := services.Get(i).Methods() + for j := 0; j < methods.Len(); j++ { + method := methods.Get(j) + if assistantEnabled(method) && messageContainsSensitiveField(method.Input(), map[protoreflect.FullName]bool{}) { + t.Errorf("assistant playground method %s transitively accepts a sensitive field", method.FullName()) + } + } + } + return true + }) +} + +func serviceVisibility(service protoreflect.ServiceDescriptor) optionsv1.ApiVisibility { + options, ok := service.Options().(*descriptorpb.ServiceOptions) + if !ok || !proto.HasExtension(options, optionsv1.E_ServiceApiVisibility) { + return optionsv1.ApiVisibility_API_VISIBILITY_UNSPECIFIED + } + visibility, ok := proto.GetExtension(options, optionsv1.E_ServiceApiVisibility).(optionsv1.ApiVisibility) + if !ok { + return optionsv1.ApiVisibility_API_VISIBILITY_UNSPECIFIED + } + return visibility +} + +func methodVisibility(method protoreflect.MethodDescriptor) optionsv1.ApiVisibility { + if visibility, explicit := explicitMethodVisibility(method); explicit { + return visibility + } + return serviceVisibility(method.Parent().(protoreflect.ServiceDescriptor)) +} + +func explicitMethodVisibility(method protoreflect.MethodDescriptor) (optionsv1.ApiVisibility, bool) { + options, ok := method.Options().(*descriptorpb.MethodOptions) + if ok && proto.HasExtension(options, optionsv1.E_MethodApiVisibility) { + if visibility, valid := proto.GetExtension(options, optionsv1.E_MethodApiVisibility).(optionsv1.ApiVisibility); valid { + return visibility, true + } + } + return optionsv1.ApiVisibility_API_VISIBILITY_UNSPECIFIED, false +} + +func assistantEnabled(method protoreflect.MethodDescriptor) bool { + options, ok := method.Options().(*descriptorpb.MethodOptions) + if !ok || !proto.HasExtension(options, optionsv1.E_AssistantPlayground) { + return false + } + enabled, _ := proto.GetExtension(options, optionsv1.E_AssistantPlayground).(bool) + return enabled +} + +func messageContainsSensitiveField(message protoreflect.MessageDescriptor, visiting map[protoreflect.FullName]bool) bool { + if visiting[message.FullName()] { + return false + } + visiting[message.FullName()] = true + defer delete(visiting, message.FullName()) + + fields := message.Fields() + for i := 0; i < fields.Len(); i++ { + field := fields.Get(i) + options, ok := field.Options().(*descriptorpb.FieldOptions) + if ok && proto.HasExtension(options, optionsv1.E_Sensitive) { + if sensitive, valid := proto.GetExtension(options, optionsv1.E_Sensitive).(bool); valid && sensitive { + return true + } + } + if nested := field.Message(); nested != nil && messageContainsSensitiveField(nested, visiting) { + return true + } + } + return false +} diff --git a/libops/v1/options/visibility.pb.go b/libops/v1/options/visibility.pb.go new file mode 100644 index 0000000..6cd6060 --- /dev/null +++ b/libops/v1/options/visibility.pb.go @@ -0,0 +1,174 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc (unknown) +// source: libops/v1/options/visibility.proto + +package options + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + descriptorpb "google.golang.org/protobuf/types/descriptorpb" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// ApiVisibility controls which API surface may be published to customers. +// Services must declare a visibility explicitly. Methods inherit their service +// visibility unless they declare a narrower override. +type ApiVisibility int32 + +const ( + ApiVisibility_API_VISIBILITY_UNSPECIFIED ApiVisibility = 0 + ApiVisibility_API_VISIBILITY_PUBLIC ApiVisibility = 1 + ApiVisibility_API_VISIBILITY_INTERNAL ApiVisibility = 2 +) + +// Enum value maps for ApiVisibility. +var ( + ApiVisibility_name = map[int32]string{ + 0: "API_VISIBILITY_UNSPECIFIED", + 1: "API_VISIBILITY_PUBLIC", + 2: "API_VISIBILITY_INTERNAL", + } + ApiVisibility_value = map[string]int32{ + "API_VISIBILITY_UNSPECIFIED": 0, + "API_VISIBILITY_PUBLIC": 1, + "API_VISIBILITY_INTERNAL": 2, + } +) + +func (x ApiVisibility) Enum() *ApiVisibility { + p := new(ApiVisibility) + *p = x + return p +} + +func (x ApiVisibility) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ApiVisibility) Descriptor() protoreflect.EnumDescriptor { + return file_libops_v1_options_visibility_proto_enumTypes[0].Descriptor() +} + +func (ApiVisibility) Type() protoreflect.EnumType { + return &file_libops_v1_options_visibility_proto_enumTypes[0] +} + +func (x ApiVisibility) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ApiVisibility.Descriptor instead. +func (ApiVisibility) EnumDescriptor() ([]byte, []int) { + return file_libops_v1_options_visibility_proto_rawDescGZIP(), []int{0} +} + +var file_libops_v1_options_visibility_proto_extTypes = []protoimpl.ExtensionInfo{ + { + ExtendedType: (*descriptorpb.ServiceOptions)(nil), + ExtensionType: (*ApiVisibility)(nil), + Field: 50004, + Name: "libops.v1.options.service_api_visibility", + Tag: "varint,50004,opt,name=service_api_visibility,enum=libops.v1.options.ApiVisibility", + Filename: "libops/v1/options/visibility.proto", + }, + { + ExtendedType: (*descriptorpb.MethodOptions)(nil), + ExtensionType: (*ApiVisibility)(nil), + Field: 50005, + Name: "libops.v1.options.method_api_visibility", + Tag: "varint,50005,opt,name=method_api_visibility,enum=libops.v1.options.ApiVisibility", + Filename: "libops/v1/options/visibility.proto", + }, +} + +// Extension fields to descriptorpb.ServiceOptions. +var ( + // optional libops.v1.options.ApiVisibility service_api_visibility = 50004; + E_ServiceApiVisibility = &file_libops_v1_options_visibility_proto_extTypes[0] +) + +// Extension fields to descriptorpb.MethodOptions. +var ( + // optional libops.v1.options.ApiVisibility method_api_visibility = 50005; + E_MethodApiVisibility = &file_libops_v1_options_visibility_proto_extTypes[1] +) + +var File_libops_v1_options_visibility_proto protoreflect.FileDescriptor + +const file_libops_v1_options_visibility_proto_rawDesc = "" + + "\n" + + "\"libops/v1/options/visibility.proto\x12\x11libops.v1.options\x1a google/protobuf/descriptor.proto*g\n" + + "\rApiVisibility\x12\x1e\n" + + "\x1aAPI_VISIBILITY_UNSPECIFIED\x10\x00\x12\x19\n" + + "\x15API_VISIBILITY_PUBLIC\x10\x01\x12\x1b\n" + + "\x17API_VISIBILITY_INTERNAL\x10\x02:y\n" + + "\x16service_api_visibility\x12\x1f.google.protobuf.ServiceOptions\x18Ԇ\x03 \x01(\x0e2 .libops.v1.options.ApiVisibilityR\x14serviceApiVisibility:v\n" + + "\x15method_api_visibility\x12\x1e.google.protobuf.MethodOptions\x18Ն\x03 \x01(\x0e2 .libops.v1.options.ApiVisibilityR\x13methodApiVisibilityB\xb9\x01\n" + + "\x15com.libops.v1.optionsB\x0fVisibilityProtoP\x01Z)github.com/libops/proto/libops/v1/options\xa2\x02\x03LVO\xaa\x02\x11Libops.V1.Options\xca\x02\x11Libops\\V1\\Options\xe2\x02\x1dLibops\\V1\\Options\\GPBMetadata\xea\x02\x13Libops::V1::Optionsb\x06proto3" + +var ( + file_libops_v1_options_visibility_proto_rawDescOnce sync.Once + file_libops_v1_options_visibility_proto_rawDescData []byte +) + +func file_libops_v1_options_visibility_proto_rawDescGZIP() []byte { + file_libops_v1_options_visibility_proto_rawDescOnce.Do(func() { + file_libops_v1_options_visibility_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_libops_v1_options_visibility_proto_rawDesc), len(file_libops_v1_options_visibility_proto_rawDesc))) + }) + return file_libops_v1_options_visibility_proto_rawDescData +} + +var file_libops_v1_options_visibility_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_libops_v1_options_visibility_proto_goTypes = []any{ + (ApiVisibility)(0), // 0: libops.v1.options.ApiVisibility + (*descriptorpb.ServiceOptions)(nil), // 1: google.protobuf.ServiceOptions + (*descriptorpb.MethodOptions)(nil), // 2: google.protobuf.MethodOptions +} +var file_libops_v1_options_visibility_proto_depIdxs = []int32{ + 1, // 0: libops.v1.options.service_api_visibility:extendee -> google.protobuf.ServiceOptions + 2, // 1: libops.v1.options.method_api_visibility:extendee -> google.protobuf.MethodOptions + 0, // 2: libops.v1.options.service_api_visibility:type_name -> libops.v1.options.ApiVisibility + 0, // 3: libops.v1.options.method_api_visibility:type_name -> libops.v1.options.ApiVisibility + 4, // [4:4] is the sub-list for method output_type + 4, // [4:4] is the sub-list for method input_type + 2, // [2:4] is the sub-list for extension type_name + 0, // [0:2] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_libops_v1_options_visibility_proto_init() } +func file_libops_v1_options_visibility_proto_init() { + if File_libops_v1_options_visibility_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_libops_v1_options_visibility_proto_rawDesc), len(file_libops_v1_options_visibility_proto_rawDesc)), + NumEnums: 1, + NumMessages: 0, + NumExtensions: 2, + NumServices: 0, + }, + GoTypes: file_libops_v1_options_visibility_proto_goTypes, + DependencyIndexes: file_libops_v1_options_visibility_proto_depIdxs, + EnumInfos: file_libops_v1_options_visibility_proto_enumTypes, + ExtensionInfos: file_libops_v1_options_visibility_proto_extTypes, + }.Build() + File_libops_v1_options_visibility_proto = out.File + file_libops_v1_options_visibility_proto_goTypes = nil + file_libops_v1_options_visibility_proto_depIdxs = nil +} diff --git a/libops/v1/options/visibility.proto b/libops/v1/options/visibility.proto new file mode 100644 index 0000000..fd8f635 --- /dev/null +++ b/libops/v1/options/visibility.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; + +package libops.v1.options; + +import "google/protobuf/descriptor.proto"; + +option go_package = "github.com/libops/proto/libops/v1/options;optionsv1"; + +// ApiVisibility controls which API surface may be published to customers. +// Services must declare a visibility explicitly. Methods inherit their service +// visibility unless they declare a narrower override. +enum ApiVisibility { + API_VISIBILITY_UNSPECIFIED = 0; + API_VISIBILITY_PUBLIC = 1; + API_VISIBILITY_INTERNAL = 2; +} + +extend google.protobuf.ServiceOptions { + ApiVisibility service_api_visibility = 50004; +} + +extend google.protobuf.MethodOptions { + ApiVisibility method_api_visibility = 50005; +} diff --git a/libops/v1/organization_account_api.pb.go b/libops/v1/organization_account_api.pb.go index 7985b0c..c90a896 100644 --- a/libops/v1/organization_account_api.pb.go +++ b/libops/v1/organization_account_api.pb.go @@ -11,7 +11,6 @@ import ( _ "github.com/libops/proto/libops/v1/options" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - _ "google.golang.org/protobuf/types/descriptorpb" reflect "reflect" sync "sync" unsafe "unsafe" @@ -620,7 +619,7 @@ var File_libops_v1_organization_account_api_proto protoreflect.FileDescriptor const file_libops_v1_organization_account_api_proto_rawDesc = "" + "\n" + - "(libops/v1/organization_account_api.proto\x12\tlibops.v1\x1a google/protobuf/descriptor.proto\x1a\x1dlibops/v1/options/scope.proto\x1a\x1clibops/v1/common/types.proto\"\xb9\x01\n" + + "(libops/v1/organization_account_api.proto\x12\tlibops.v1\x1a\x1dlibops/v1/options/scope.proto\x1a\"libops/v1/options/visibility.proto\x1a\x1clibops/v1/common/types.proto\"\xb9\x01\n" + "\x13OrganizationAccount\x12\x1d\n" + "\n" + "account_id\x18\x01 \x01(\tR\taccountId\x12\x14\n" + @@ -668,14 +667,14 @@ const file_libops_v1_organization_account_api_proto_rawDesc = "" + "\n" + "api_key_id\x18\x01 \x01(\tR\bapiKeyId\"0\n" + "\x14RevokeApiKeyResponse\x12\x18\n" + - "\asuccess\x18\x01 \x01(\bR\asuccess2\xbe\x03\n" + + "\asuccess\x18\x01 \x01(\bR\asuccess2\xc4\x03\n" + "\x0eAccountService\x12x\n" + "\x11GetAccountByEmail\x12#.libops.v1.GetAccountByEmailRequest\x1a$.libops.v1.GetAccountByEmailResponse\"\x18\x92\xb5\x18\x11\b\x02\x10\x01\x18\x01\"\tread:user\x90\x02\x01\x12e\n" + "\fCreateApiKey\x12\x1e.libops.v1.CreateApiKeyRequest\x1a\x1f.libops.v1.CreateApiKeyResponse\"\x14\x92\xb5\x18\x10\b\x02\x10\x02\"\n" + "write:user\x12d\n" + "\vListApiKeys\x12\x1d.libops.v1.ListApiKeysRequest\x1a\x1e.libops.v1.ListApiKeysResponse\"\x16\x92\xb5\x18\x0f\b\x02\x10\x01\"\tread:user\x90\x02\x01\x12e\n" + "\fRevokeApiKey\x12\x1e.libops.v1.RevokeApiKeyRequest\x1a\x1f.libops.v1.RevokeApiKeyResponse\"\x14\x92\xb5\x18\x10\b\x02\x10\x02\"\n" + - "write:userB\x9d\x01\n" + + "write:user\x1a\x04\xa0\xb5\x18\x01B\x9d\x01\n" + "\rcom.libops.v1B\x1bOrganizationAccountApiProtoP\x01Z*github.com/libops/proto/libops/v1;libopsv1\xa2\x02\x03LXX\xaa\x02\tLibops.V1\xca\x02\tLibops\\V1\xe2\x02\x15Libops\\V1\\GPBMetadata\xea\x02\n" + "Libops::V1b\x06proto3" diff --git a/libops/v1/organization_account_api.proto b/libops/v1/organization_account_api.proto index 234aa4a..45d381f 100644 --- a/libops/v1/organization_account_api.proto +++ b/libops/v1/organization_account_api.proto @@ -2,14 +2,16 @@ syntax = "proto3"; package libops.v1; -import "google/protobuf/descriptor.proto"; import "libops/v1/options/scope.proto"; +import "libops/v1/options/visibility.proto"; import "libops/v1/common/types.proto"; option go_package = "github.com/libops/proto/libops/v1;libopsv1"; // AccountService provides limited account lookup for authenticated users service AccountService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_PUBLIC; + // Get account information by email (for Terraform provider lookups) rpc GetAccountByEmail(GetAccountByEmailRequest) returns (GetAccountByEmailResponse) { option idempotency_level = NO_SIDE_EFFECTS; diff --git a/libops/v1/organization_api.pb.go b/libops/v1/organization_api.pb.go index b7e08c6..6bd8898 100644 --- a/libops/v1/organization_api.pb.go +++ b/libops/v1/organization_api.pb.go @@ -11,7 +11,6 @@ import ( _ "github.com/libops/proto/libops/v1/options" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - _ "google.golang.org/protobuf/types/descriptorpb" emptypb "google.golang.org/protobuf/types/known/emptypb" fieldmaskpb "google.golang.org/protobuf/types/known/fieldmaskpb" reflect "reflect" @@ -5454,7 +5453,7 @@ var File_libops_v1_organization_api_proto protoreflect.FileDescriptor const file_libops_v1_organization_api_proto_rawDesc = "" + "\n" + - " libops/v1/organization_api.proto\x12\tlibops.v1\x1a google/protobuf/descriptor.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\x1a\x1elibops/v1/common/project.proto\x1a#libops/v1/common/organization.proto\x1a\x1blibops/v1/common/site.proto\x1a\x1clibops/v1/common/types.proto\x1a!libops/v1/options/assistant.proto\x1a\x1dlibops/v1/options/scope.proto\"[\n" + + " libops/v1/organization_api.proto\x12\tlibops.v1\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\x1a\x1elibops/v1/common/project.proto\x1a#libops/v1/common/organization.proto\x1a\x1blibops/v1/common/site.proto\x1a\x1clibops/v1/common/types.proto\x1a!libops/v1/options/assistant.proto\x1a\x1dlibops/v1/options/scope.proto\x1a\"libops/v1/options/visibility.proto\"[\n" + "\x11GetProjectRequest\x12'\n" + "\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x1d\n" + "\n" + @@ -5851,14 +5850,14 @@ const file_libops_v1_organization_api_proto_rawDesc = "" + "\x1eFIREWALL_RULE_TYPE_UNSPECIFIED\x10\x00\x12$\n" + " FIREWALL_RULE_TYPE_HTTPS_ALLOWED\x10\x01\x12\"\n" + "\x1eFIREWALL_RULE_TYPE_SSH_ALLOWED\x10\x02\x12\x1e\n" + - "\x1aFIREWALL_RULE_TYPE_BLOCKED\x10\x032\xea\x06\n" + + "\x1aFIREWALL_RULE_TYPE_BLOCKED\x10\x032\xf0\x06\n" + "\x13OrganizationService\x12\x8b\x01\n" + "\x0fGetOrganization\x12!.libops.v1.GetOrganizationRequest\x1a\".libops.v1.GetOrganizationResponse\"1\x92\xb5\x18*\b\x03\x10\x01\x18\x01\"\x11read:organization*\x0forganization_id\x90\x02\x01\x12\x81\x01\n" + "\x12CreateOrganization\x12$.libops.v1.CreateOrganizationRequest\x1a%.libops.v1.CreateOrganizationResponse\"\x1e\x92\xb5\x18\x1a\b\x02\x10\x02\x18\x01\"\x12write:organization\x12\x92\x01\n" + "\x12UpdateOrganization\x12$.libops.v1.UpdateOrganizationRequest\x1a%.libops.v1.UpdateOrganizationResponse\"/\x92\xb5\x18+\b\x03\x10\x02\x18\x01\"\x12write:organization*\x0forganization_id\x12\x84\x01\n" + "\x12DeleteOrganization\x12$.libops.v1.DeleteOrganizationRequest\x1a\x16.google.protobuf.Empty\"0\x92\xb5\x18,\b\x03\x10\x03\x18\x01\"\x13delete:organization*\x0forganization_id\x12\x80\x01\n" + "\x11ListOrganizations\x12#.libops.v1.ListOrganizationsRequest\x1a$.libops.v1.ListOrganizationsResponse\" \x92\xb5\x18\x19\b\x02\x10\x01\x18\x01\"\x11read:organization\x90\x02\x01\x12\xa1\x01\n" + - "\x18ListOrganizationProjects\x12*.libops.v1.ListOrganizationProjectsRequest\x1a+.libops.v1.ListOrganizationProjectsResponse\",\x92\xb5\x18%\b\x03\x10\x01\x18\x01\"\fread:project*\x0forganization_id\x90\x02\x012\xa6\x04\n" + + "\x18ListOrganizationProjects\x12*.libops.v1.ListOrganizationProjectsRequest\x1a+.libops.v1.ListOrganizationProjectsResponse\",\x92\xb5\x18%\b\x03\x10\x01\x18\x01\"\fread:project*\x0forganization_id\x90\x02\x01\x1a\x04\xa0\xb5\x18\x012\xac\x04\n" + "\vSiteService\x12`\n" + "\tListSites\x12\x1b.libops.v1.ListSitesRequest\x1a\x1c.libops.v1.ListSitesResponse\"\x18\x92\xb5\x18\x11\b\x02\x10\x01\x18\x01\"\tread:site\x90\x02\x01\x12c\n" + "\aGetSite\x12\x19.libops.v1.GetSiteRequest\x1a\x1a.libops.v1.GetSiteResponse\"!\x92\xb5\x18\x1a\b\x05\x10\x01\x18\x01\"\tread:site*\asite_id\x90\x02\x01\x12v\n" + @@ -5869,7 +5868,7 @@ const file_libops_v1_organization_api_proto_rawDesc = "" + "UpdateSite\x12\x1c.libops.v1.UpdateSiteRequest\x1a\x1d.libops.v1.UpdateSiteResponse\"#\x92\xb5\x18\x1b\b\x05\x10\x02\x18\x01\"\n" + "write:site*\asite_id\x98\xb5\x18\x01\x12h\n" + "\n" + - "DeleteSite\x12\x1c.libops.v1.DeleteSiteRequest\x1a\x16.google.protobuf.Empty\"$\x92\xb5\x18\x1c\b\x05\x10\x03\x18\x01\"\vdelete:site*\asite_id\x98\xb5\x18\x012\x95\x06\n" + + "DeleteSite\x12\x1c.libops.v1.DeleteSiteRequest\x1a\x16.google.protobuf.Empty\"$\x92\xb5\x18\x1c\b\x05\x10\x03\x18\x01\"\vdelete:site*\asite_id\x98\xb5\x18\x01\x1a\x04\xa0\xb5\x18\x012\x9b\x06\n" + "\rDomainService\x12{\n" + "\x0fListSiteDomains\x12!.libops.v1.ListSiteDomainsRequest\x1a\".libops.v1.ListSiteDomainsResponse\"!\x92\xb5\x18\x1a\b\x05\x10\x01\x18\x01\"\tread:site*\asite_id\x90\x02\x01\x12~\n" + "\x10CreateSiteDomain\x12\".libops.v1.CreateSiteDomainRequest\x1a#.libops.v1.CreateSiteDomainResponse\"!\x92\xb5\x18\x1d\b\x05\x10\x02\x18\x01\"\n" + @@ -5880,7 +5879,7 @@ const file_libops_v1_organization_api_proto_rawDesc = "" + "\x1bRetrySiteDomainProvisioning\x12-.libops.v1.RetrySiteDomainProvisioningRequest\x1a..libops.v1.RetrySiteDomainProvisioningResponse\"\"\x92\xb5\x18\x1b\b\x05\x10\x02\x18\x01\"\n" + "write:site*\asite_id\x90\x02\x02\x12o\n" + "\x10DeleteSiteDomain\x12\".libops.v1.DeleteSiteDomainRequest\x1a\x16.google.protobuf.Empty\"\x1f\x92\xb5\x18\x1b\b\x05\x10\x02\x18\x01\"\n" + - "write:site*\asite_id2\xac\t\n" + + "write:site*\asite_id\x1a\x04\xa0\xb5\x18\x012\xb2\t\n" + "\x0eProjectService\x12r\n" + "\n" + "GetProject\x12\x1c.libops.v1.GetProjectRequest\x1a\x1d.libops.v1.GetProjectResponse\"'\x92\xb5\x18 \b\x04\x10\x01\x18\x01\"\fread:project*\n" + @@ -5898,27 +5897,27 @@ const file_libops_v1_organization_api_proto_rawDesc = "" + "\x11GetProjectRuntime\x12#.libops.v1.GetProjectRuntimeRequest\x1a$.libops.v1.GetProjectRuntimeResponse\"'\x92\xb5\x18 \b\x04\x10\x01\x18\x01\"\fread:project*\n" + "project_id\x90\x02\x01\x12\x90\x01\n" + "\x14CreateProjectRuntime\x12&.libops.v1.CreateProjectRuntimeRequest\x1a'.libops.v1.CreateProjectRuntimeResponse\"'\x92\xb5\x18#\b\x04\x10\x02\x18\x01\"\rwrite:project2\n" + - "project_id8\x042\x9f\x04\n" + + "project_id8\x04\x1a\x04\xa0\xb5\x18\x012\xa5\x04\n" + "\x0fFirewallService\x12\xb1\x01\n" + "\x1dListOrganizationFirewallRules\x12/.libops.v1.ListOrganizationFirewallRulesRequest\x1a0.libops.v1.ListOrganizationFirewallRulesResponse\"-\x92\xb5\x18&\b\x03\x10\x01\x18\x01\"\rread:firewall*\x0forganization_id\x90\x02\x01\x12\xb8\x01\n" + "\x1eCreateOrganizationFirewallRule\x120.libops.v1.CreateOrganizationFirewallRuleRequest\x1a1.libops.v1.CreateOrganizationFirewallRuleResponse\"1\x92\xb5\x18)\b\x03\x10\x02\x18\x01\"\x0ewrite:firewall2\x0forganization_id8\x03\x98\xb5\x18\x01\x12\x9c\x01\n" + - "\x1eDeleteOrganizationFirewallRule\x120.libops.v1.DeleteOrganizationFirewallRuleRequest\x1a\x16.google.protobuf.Empty\"0\x92\xb5\x18(\b\x03\x10\x02\x18\x01\"\x0fdelete:firewall*\x0forganization_id\x98\xb5\x18\x012\xef\x03\n" + + "\x1eDeleteOrganizationFirewallRule\x120.libops.v1.DeleteOrganizationFirewallRuleRequest\x1a\x16.google.protobuf.Empty\"0\x92\xb5\x18(\b\x03\x10\x02\x18\x01\"\x0fdelete:firewall*\x0forganization_id\x98\xb5\x18\x01\x1a\x04\xa0\xb5\x18\x012\xf5\x03\n" + "\x16ProjectFirewallService\x12\x9d\x01\n" + "\x18ListProjectFirewallRules\x12*.libops.v1.ListProjectFirewallRulesRequest\x1a+.libops.v1.ListProjectFirewallRulesResponse\"(\x92\xb5\x18!\b\x04\x10\x01\x18\x01\"\rread:firewall*\n" + "project_id\x90\x02\x01\x12\xa4\x01\n" + "\x19CreateProjectFirewallRule\x12+.libops.v1.CreateProjectFirewallRuleRequest\x1a,.libops.v1.CreateProjectFirewallRuleResponse\",\x92\xb5\x18$\b\x04\x10\x02\x18\x01\"\x0ewrite:firewall2\n" + "project_id8\x04\x98\xb5\x18\x01\x12\x8d\x01\n" + "\x19DeleteProjectFirewallRule\x12+.libops.v1.DeleteProjectFirewallRuleRequest\x1a\x16.google.protobuf.Empty\"+\x92\xb5\x18#\b\x04\x10\x02\x18\x01\"\x0fdelete:firewall*\n" + - "project_id\x98\xb5\x18\x012\xcb\x03\n" + + "project_id\x98\xb5\x18\x01\x1a\x04\xa0\xb5\x18\x012\xd1\x03\n" + "\x13SiteFirewallService\x12\x91\x01\n" + "\x15ListSiteFirewallRules\x12'.libops.v1.ListSiteFirewallRulesRequest\x1a(.libops.v1.ListSiteFirewallRulesResponse\"%\x92\xb5\x18\x1e\b\x05\x10\x01\x18\x01\"\rread:firewall*\asite_id\x90\x02\x01\x12\x98\x01\n" + "\x16CreateSiteFirewallRule\x12(.libops.v1.CreateSiteFirewallRuleRequest\x1a).libops.v1.CreateSiteFirewallRuleResponse\")\x92\xb5\x18!\b\x05\x10\x02\x18\x01\"\x0ewrite:firewall2\asite_id8\x05\x98\xb5\x18\x01\x12\x84\x01\n" + - "\x16DeleteSiteFirewallRule\x12(.libops.v1.DeleteSiteFirewallRuleRequest\x1a\x16.google.protobuf.Empty\"(\x92\xb5\x18 \b\x05\x10\x02\x18\x01\"\x0fdelete:firewall*\asite_id\x98\xb5\x18\x012\x90\x05\n" + + "\x16DeleteSiteFirewallRule\x12(.libops.v1.DeleteSiteFirewallRuleRequest\x1a\x16.google.protobuf.Empty\"(\x92\xb5\x18 \b\x05\x10\x02\x18\x01\"\x0fdelete:firewall*\asite_id\x98\xb5\x18\x01\x1a\x04\xa0\xb5\x18\x012\x96\x05\n" + "\rMemberService\x12\x9e\x01\n" + "\x17ListOrganizationMembers\x12).libops.v1.ListOrganizationMembersRequest\x1a*.libops.v1.ListOrganizationMembersResponse\",\x92\xb5\x18%\b\x03\x10\x01\x18\x01\"\fread:members*\x0forganization_id\x90\x02\x01\x12\xa5\x01\n" + "\x18CreateOrganizationMember\x12*.libops.v1.CreateOrganizationMemberRequest\x1a+.libops.v1.CreateOrganizationMemberResponse\"0\x92\xb5\x18(\b\x03\x10\x03\x18\x01\"\rwrite:members2\x0forganization_id8\x03\x98\xb5\x18\x01\x12\xa3\x01\n" + "\x18UpdateOrganizationMember\x12*.libops.v1.UpdateOrganizationMemberRequest\x1a+.libops.v1.UpdateOrganizationMemberResponse\".\x92\xb5\x18&\b\x03\x10\x03\x18\x01\"\rwrite:members*\x0forganization_id\x98\xb5\x18\x01\x12\x8f\x01\n" + - "\x18DeleteOrganizationMember\x12*.libops.v1.DeleteOrganizationMemberRequest\x1a\x16.google.protobuf.Empty\"/\x92\xb5\x18'\b\x03\x10\x03\x18\x01\"\x0edelete:members*\x0forganization_id\x98\xb5\x18\x012\xcc\x04\n" + + "\x18DeleteOrganizationMember\x12*.libops.v1.DeleteOrganizationMemberRequest\x1a\x16.google.protobuf.Empty\"/\x92\xb5\x18'\b\x03\x10\x03\x18\x01\"\x0edelete:members*\x0forganization_id\x98\xb5\x18\x01\x1a\x04\xa0\xb5\x18\x012\xd2\x04\n" + "\x14ProjectMemberService\x12\x8a\x01\n" + "\x12ListProjectMembers\x12$.libops.v1.ListProjectMembersRequest\x1a%.libops.v1.ListProjectMembersResponse\"'\x92\xb5\x18 \b\x04\x10\x01\x18\x01\"\fread:members*\n" + "project_id\x90\x02\x01\x12\x91\x01\n" + @@ -5927,23 +5926,23 @@ const file_libops_v1_organization_api_proto_rawDesc = "" + "\x13UpdateProjectMember\x12%.libops.v1.UpdateProjectMemberRequest\x1a&.libops.v1.UpdateProjectMemberResponse\")\x92\xb5\x18!\b\x04\x10\x03\x18\x01\"\rwrite:members*\n" + "project_id\x98\xb5\x18\x01\x12\x80\x01\n" + "\x13DeleteProjectMember\x12%.libops.v1.DeleteProjectMemberRequest\x1a\x16.google.protobuf.Empty\"*\x92\xb5\x18\"\b\x04\x10\x03\x18\x01\"\x0edelete:members*\n" + - "project_id\x98\xb5\x18\x012\x9a\x04\n" + + "project_id\x98\xb5\x18\x01\x1a\x04\xa0\xb5\x18\x012\xa0\x04\n" + "\x11SiteMemberService\x12~\n" + "\x0fListSiteMembers\x12!.libops.v1.ListSiteMembersRequest\x1a\".libops.v1.ListSiteMembersResponse\"$\x92\xb5\x18\x1d\b\x05\x10\x01\x18\x01\"\fread:members*\asite_id\x90\x02\x01\x12\x85\x01\n" + "\x10CreateSiteMember\x12\".libops.v1.CreateSiteMemberRequest\x1a#.libops.v1.CreateSiteMemberResponse\"(\x92\xb5\x18 \b\x05\x10\x03\x18\x01\"\rwrite:members2\asite_id8\x05\x98\xb5\x18\x01\x12\x83\x01\n" + "\x10UpdateSiteMember\x12\".libops.v1.UpdateSiteMemberRequest\x1a#.libops.v1.UpdateSiteMemberResponse\"&\x92\xb5\x18\x1e\b\x05\x10\x03\x18\x01\"\rwrite:members*\asite_id\x98\xb5\x18\x01\x12w\n" + - "\x10DeleteSiteMember\x12\".libops.v1.DeleteSiteMemberRequest\x1a\x16.google.protobuf.Empty\"'\x92\xb5\x18\x1f\b\x05\x10\x03\x18\x01\"\x0edelete:members*\asite_id\x98\xb5\x18\x012\xc0\x02\n" + + "\x10DeleteSiteMember\x12\".libops.v1.DeleteSiteMemberRequest\x1a\x16.google.protobuf.Empty\"'\x92\xb5\x18\x1f\b\x05\x10\x03\x18\x01\"\x0edelete:members*\asite_id\x98\xb5\x18\x01\x1a\x04\xa0\xb5\x18\x012\xc6\x02\n" + "\rSshKeyService\x12f\n" + "\vListSshKeys\x12\x1d.libops.v1.ListSshKeysRequest\x1a\x1e.libops.v1.ListSshKeysResponse\"\x18\x92\xb5\x18\x11\b\x02\x10\x01\x18\x01\"\tread:user\x90\x02\x01\x12g\n" + "\fCreateSshKey\x12\x1e.libops.v1.CreateSshKeyRequest\x1a\x1f.libops.v1.CreateSshKeyResponse\"\x16\x92\xb5\x18\x12\b\x02\x10\x02\x18\x01\"\n" + "write:user\x12^\n" + "\fDeleteSshKey\x12\x1e.libops.v1.DeleteSshKeyRequest\x1a\x16.google.protobuf.Empty\"\x16\x92\xb5\x18\x12\b\x02\x10\x02\x18\x01\"\n" + - "write:user2\xfe\x01\n" + + "write:user\x1a\x04\xa0\xb5\x18\x012\x84\x02\n" + "\x15SiteOperationsService\x12u\n" + "\rGetSiteStatus\x12\x1f.libops.v1.GetSiteStatusRequest\x1a .libops.v1.GetSiteStatusResponse\"!\x92\xb5\x18\x1a\b\x05\x10\x01\x18\x01\"\tread:site*\asite_id\x90\x02\x01\x12n\n" + "\n" + "DeploySite\x12\x1c.libops.v1.DeploySiteRequest\x1a\x1d.libops.v1.DeploySiteResponse\"#\x92\xb5\x18\x1b\b\x05\x10\x02\x18\x01\"\n" + - "write:site*\asite_id\x98\xb5\x18\x01B\x96\x01\n" + + "write:site*\asite_id\x98\xb5\x18\x01\x1a\x04\xa0\xb5\x18\x01B\x96\x01\n" + "\rcom.libops.v1B\x14OrganizationApiProtoP\x01Z*github.com/libops/proto/libops/v1;libopsv1\xa2\x02\x03LXX\xaa\x02\tLibops.V1\xca\x02\tLibops\\V1\xe2\x02\x15Libops\\V1\\GPBMetadata\xea\x02\n" + "Libops::V1b\x06proto3" diff --git a/libops/v1/organization_api.proto b/libops/v1/organization_api.proto index f16b6ff..2a0c428 100644 --- a/libops/v1/organization_api.proto +++ b/libops/v1/organization_api.proto @@ -2,7 +2,6 @@ syntax = "proto3"; package libops.v1; -import "google/protobuf/descriptor.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/field_mask.proto"; import "libops/v1/common/project.proto"; @@ -11,11 +10,14 @@ import "libops/v1/common/site.proto"; import "libops/v1/common/types.proto"; import "libops/v1/options/assistant.proto"; import "libops/v1/options/scope.proto"; +import "libops/v1/options/visibility.proto"; option go_package = "github.com/libops/proto/libops/v1;libopsv1"; // OrganizationService manages organization-facing organization/folder operations service OrganizationService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_PUBLIC; + // Get organization configuration (organization view) rpc GetOrganization(GetOrganizationRequest) returns (GetOrganizationResponse) { option idempotency_level = NO_SIDE_EFFECTS; @@ -87,6 +89,8 @@ service OrganizationService { // SiteService manages organization-facing site operations service SiteService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_PUBLIC; + // List sites for a organization rpc ListSites(ListSitesRequest) returns (ListSitesResponse) { option idempotency_level = NO_SIDE_EFFECTS; @@ -151,6 +155,8 @@ service SiteService { // DomainService manages custom domains for sites. service DomainService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_PUBLIC; + rpc ListSiteDomains(ListSiteDomainsRequest) returns (ListSiteDomainsResponse) { option idempotency_level = NO_SIDE_EFFECTS; option (libops.v1.options.required_scope) = { @@ -213,6 +219,8 @@ service DomainService { // ProjectService manages organization-facing project operations service ProjectService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_PUBLIC; + // Get project configuration (organization view) rpc GetProject(GetProjectRequest) returns (GetProjectResponse) { option idempotency_level = NO_SIDE_EFFECTS; @@ -636,6 +644,8 @@ message DeleteSiteDomainRequest { // FirewallService manages firewall operations for all sites for a organization service FirewallService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_PUBLIC; + // List firewall rules applied to all sites for a organization rpc ListOrganizationFirewallRules(ListOrganizationFirewallRulesRequest) returns (ListOrganizationFirewallRulesResponse) { option idempotency_level = NO_SIDE_EFFECTS; @@ -673,6 +683,8 @@ service FirewallService { // ProjectFirewallService manages firewall operations for all sites in a project service ProjectFirewallService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_PUBLIC; + // List firewall rules applied to all sites in a project rpc ListProjectFirewallRules(ListProjectFirewallRulesRequest) returns (ListProjectFirewallRulesResponse) { option idempotency_level = NO_SIDE_EFFECTS; @@ -710,6 +722,8 @@ service ProjectFirewallService { // SiteFirewallService manages firewall operations for a specific site service SiteFirewallService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_PUBLIC; + // List firewall rules applied to a specific site rpc ListSiteFirewallRules(ListSiteFirewallRulesRequest) returns (ListSiteFirewallRulesResponse) { option idempotency_level = NO_SIDE_EFFECTS; @@ -751,6 +765,8 @@ service SiteFirewallService { // MemberService manages organization membership operations service MemberService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_PUBLIC; + // List members of a organization rpc ListOrganizationMembers(ListOrganizationMembersRequest) returns (ListOrganizationMembersResponse) { option idempotency_level = NO_SIDE_EFFECTS; @@ -799,6 +815,8 @@ service MemberService { // ProjectMemberService manages project membership operations service ProjectMemberService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_PUBLIC; + // List members of a project rpc ListProjectMembers(ListProjectMembersRequest) returns (ListProjectMembersResponse) { option idempotency_level = NO_SIDE_EFFECTS; @@ -847,6 +865,8 @@ service ProjectMemberService { // SiteMemberService manages site membership operations service SiteMemberService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_PUBLIC; + // List members of a site rpc ListSiteMembers(ListSiteMembersRequest) returns (ListSiteMembersResponse) { option idempotency_level = NO_SIDE_EFFECTS; @@ -899,6 +919,8 @@ service SiteMemberService { // SshKeyService manages SSH keys for accounts service SshKeyService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_PUBLIC; + // List SSH keys for an account rpc ListSshKeys(ListSshKeysRequest) returns (ListSshKeysResponse) { option idempotency_level = NO_SIDE_EFFECTS; @@ -937,6 +959,8 @@ service SshKeyService { // SiteOperationsService manages site deployment and operational tasks service SiteOperationsService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_PUBLIC; + // Get site deployment status rpc GetSiteStatus(GetSiteStatusRequest) returns (GetSiteStatusResponse) { option idempotency_level = NO_SIDE_EFFECTS; diff --git a/libops/v1/secrets.pb.go b/libops/v1/secrets.pb.go index 908310d..f26958c 100644 --- a/libops/v1/secrets.pb.go +++ b/libops/v1/secrets.pb.go @@ -11,7 +11,6 @@ import ( _ "github.com/libops/proto/libops/v1/options" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - _ "google.golang.org/protobuf/types/descriptorpb" emptypb "google.golang.org/protobuf/types/known/emptypb" fieldmaskpb "google.golang.org/protobuf/types/known/fieldmaskpb" reflect "reflect" @@ -1662,7 +1661,7 @@ var File_libops_v1_secrets_proto protoreflect.FileDescriptor const file_libops_v1_secrets_proto_rawDesc = "" + "\n" + - "\x17libops/v1/secrets.proto\x12\tlibops.v1\x1a google/protobuf/descriptor.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\x1a!libops/v1/options/assistant.proto\x1a\x1dlibops/v1/options/audit.proto\x1a\x1dlibops/v1/options/scope.proto\x1a\x1clibops/v1/common/types.proto\"\xa0\x01\n" + + "\x17libops/v1/secrets.proto\x12\tlibops.v1\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\x1a!libops/v1/options/assistant.proto\x1a\x1dlibops/v1/options/audit.proto\x1a\x1dlibops/v1/options/scope.proto\x1a\"libops/v1/options/visibility.proto\x1a\x1clibops/v1/common/types.proto\"\xa0\x01\n" + "\x12OrganizationSecret\x12\x1b\n" + "\tsecret_id\x18\x01 \x01(\tR\bsecretId\x12'\n" + "\x0forganization_id\x18\x02 \x01(\tR\x0eorganizationId\x12\x12\n" + @@ -1777,30 +1776,30 @@ const file_libops_v1_secrets_proto_rawDesc = "" + "\x06secret\x18\x01 \x01(\v2\x15.libops.v1.SiteSecretR\x06secret\"O\n" + "\x17DeleteSiteSecretRequest\x12\x17\n" + "\asite_id\x18\x01 \x01(\tR\x06siteId\x12\x1b\n" + - "\tsecret_id\x18\x02 \x01(\tR\bsecretId2\xbd\x06\n" + - "\x19OrganizationSecretService\x12\xa6\x01\n" + - "\x18CreateOrganizationSecret\x12*.libops.v1.CreateOrganizationSecretRequest\x1a+.libops.v1.CreateOrganizationSecretResponse\"1\x92\xb5\x18)\b\x03\x10\x02\x18\x01\"\x0emanage_secrets2\x0forganization_id8\x03\x98\xb5\x18\x01\x12\x9a\x01\n" + + "\tsecret_id\x18\x02 \x01(\tR\bsecretId2\xbb\x06\n" + + "\x19OrganizationSecretService\x12\xa2\x01\n" + + "\x18CreateOrganizationSecret\x12*.libops.v1.CreateOrganizationSecretRequest\x1a+.libops.v1.CreateOrganizationSecretResponse\"-\x92\xb5\x18)\b\x03\x10\x02\x18\x01\"\x0emanage_secrets2\x0forganization_id8\x03\x12\x9a\x01\n" + "\x15GetOrganizationSecret\x12'.libops.v1.GetOrganizationSecretRequest\x1a(.libops.v1.GetOrganizationSecretResponse\".\x92\xb5\x18'\b\x03\x10\x02\x18\x01\"\x0emanage_secrets*\x0forganization_id\x90\x02\x01\x12\xa0\x01\n" + - "\x17ListOrganizationSecrets\x12).libops.v1.ListOrganizationSecretsRequest\x1a*.libops.v1.ListOrganizationSecretsResponse\".\x92\xb5\x18'\b\x03\x10\x02\x18\x01\"\x0emanage_secrets*\x0forganization_id\x90\x02\x01\x12\xa4\x01\n" + - "\x18UpdateOrganizationSecret\x12*.libops.v1.UpdateOrganizationSecretRequest\x1a+.libops.v1.UpdateOrganizationSecretResponse\"/\x92\xb5\x18'\b\x03\x10\x02\x18\x01\"\x0emanage_secrets*\x0forganization_id\x98\xb5\x18\x01\x12\x8f\x01\n" + - "\x18DeleteOrganizationSecret\x12*.libops.v1.DeleteOrganizationSecretRequest\x1a\x16.google.protobuf.Empty\"/\x92\xb5\x18'\b\x03\x10\x02\x18\x01\"\x0emanage_secrets*\x0forganization_id\x98\xb5\x18\x012\xd9\x05\n" + - "\x14ProjectSecretService\x12\x92\x01\n" + - "\x13CreateProjectSecret\x12%.libops.v1.CreateProjectSecretRequest\x1a&.libops.v1.CreateProjectSecretResponse\",\x92\xb5\x18$\b\x04\x10\x02\x18\x01\"\x0emanage_secrets2\n" + - "project_id8\x04\x98\xb5\x18\x01\x12\x86\x01\n" + + "\x17ListOrganizationSecrets\x12).libops.v1.ListOrganizationSecretsRequest\x1a*.libops.v1.ListOrganizationSecretsResponse\".\x92\xb5\x18'\b\x03\x10\x02\x18\x01\"\x0emanage_secrets*\x0forganization_id\x90\x02\x01\x12\xa0\x01\n" + + "\x18UpdateOrganizationSecret\x12*.libops.v1.UpdateOrganizationSecretRequest\x1a+.libops.v1.UpdateOrganizationSecretResponse\"+\x92\xb5\x18'\b\x03\x10\x02\x18\x01\"\x0emanage_secrets*\x0forganization_id\x12\x8f\x01\n" + + "\x18DeleteOrganizationSecret\x12*.libops.v1.DeleteOrganizationSecretRequest\x1a\x16.google.protobuf.Empty\"/\x92\xb5\x18'\b\x03\x10\x02\x18\x01\"\x0emanage_secrets*\x0forganization_id\x98\xb5\x18\x01\x1a\x04\xa0\xb5\x18\x012\xd7\x05\n" + + "\x14ProjectSecretService\x12\x8e\x01\n" + + "\x13CreateProjectSecret\x12%.libops.v1.CreateProjectSecretRequest\x1a&.libops.v1.CreateProjectSecretResponse\"(\x92\xb5\x18$\b\x04\x10\x02\x18\x01\"\x0emanage_secrets2\n" + + "project_id8\x04\x12\x86\x01\n" + "\x10GetProjectSecret\x12\".libops.v1.GetProjectSecretRequest\x1a#.libops.v1.GetProjectSecretResponse\")\x92\xb5\x18\"\b\x04\x10\x02\x18\x01\"\x0emanage_secrets*\n" + "project_id\x90\x02\x01\x12\x8c\x01\n" + "\x12ListProjectSecrets\x12$.libops.v1.ListProjectSecretsRequest\x1a%.libops.v1.ListProjectSecretsResponse\")\x92\xb5\x18\"\b\x04\x10\x02\x18\x01\"\x0emanage_secrets*\n" + - "project_id\x90\x02\x01\x12\x90\x01\n" + - "\x13UpdateProjectSecret\x12%.libops.v1.UpdateProjectSecretRequest\x1a&.libops.v1.UpdateProjectSecretResponse\"*\x92\xb5\x18\"\b\x04\x10\x02\x18\x01\"\x0emanage_secrets*\n" + - "project_id\x98\xb5\x18\x01\x12\x80\x01\n" + + "project_id\x90\x02\x01\x12\x8c\x01\n" + + "\x13UpdateProjectSecret\x12%.libops.v1.UpdateProjectSecretRequest\x1a&.libops.v1.UpdateProjectSecretResponse\"&\x92\xb5\x18\"\b\x04\x10\x02\x18\x01\"\x0emanage_secrets*\n" + + "project_id\x12\x80\x01\n" + "\x13DeleteProjectSecret\x12%.libops.v1.DeleteProjectSecretRequest\x1a\x16.google.protobuf.Empty\"*\x92\xb5\x18\"\b\x04\x10\x02\x18\x01\"\x0emanage_secrets*\n" + - "project_id\x98\xb5\x18\x012\x9b\x05\n" + - "\x11SiteSecretService\x12\x86\x01\n" + - "\x10CreateSiteSecret\x12\".libops.v1.CreateSiteSecretRequest\x1a#.libops.v1.CreateSiteSecretResponse\")\x92\xb5\x18!\b\x05\x10\x02\x18\x01\"\x0emanage_secrets2\asite_id8\x05\x98\xb5\x18\x01\x12z\n" + + "project_id\x98\xb5\x18\x01\x1a\x04\xa0\xb5\x18\x012\x99\x05\n" + + "\x11SiteSecretService\x12\x82\x01\n" + + "\x10CreateSiteSecret\x12\".libops.v1.CreateSiteSecretRequest\x1a#.libops.v1.CreateSiteSecretResponse\"%\x92\xb5\x18!\b\x05\x10\x02\x18\x01\"\x0emanage_secrets2\asite_id8\x05\x12z\n" + "\rGetSiteSecret\x12\x1f.libops.v1.GetSiteSecretRequest\x1a .libops.v1.GetSiteSecretResponse\"&\x92\xb5\x18\x1f\b\x05\x10\x02\x18\x01\"\x0emanage_secrets*\asite_id\x90\x02\x01\x12\x80\x01\n" + - "\x0fListSiteSecrets\x12!.libops.v1.ListSiteSecretsRequest\x1a\".libops.v1.ListSiteSecretsResponse\"&\x92\xb5\x18\x1f\b\x05\x10\x02\x18\x01\"\x0emanage_secrets*\asite_id\x90\x02\x01\x12\x84\x01\n" + - "\x10UpdateSiteSecret\x12\".libops.v1.UpdateSiteSecretRequest\x1a#.libops.v1.UpdateSiteSecretResponse\"'\x92\xb5\x18\x1f\b\x05\x10\x02\x18\x01\"\x0emanage_secrets*\asite_id\x98\xb5\x18\x01\x12w\n" + - "\x10DeleteSiteSecret\x12\".libops.v1.DeleteSiteSecretRequest\x1a\x16.google.protobuf.Empty\"'\x92\xb5\x18\x1f\b\x05\x10\x02\x18\x01\"\x0emanage_secrets*\asite_id\x98\xb5\x18\x01B\x8e\x01\n" + + "\x0fListSiteSecrets\x12!.libops.v1.ListSiteSecretsRequest\x1a\".libops.v1.ListSiteSecretsResponse\"&\x92\xb5\x18\x1f\b\x05\x10\x02\x18\x01\"\x0emanage_secrets*\asite_id\x90\x02\x01\x12\x80\x01\n" + + "\x10UpdateSiteSecret\x12\".libops.v1.UpdateSiteSecretRequest\x1a#.libops.v1.UpdateSiteSecretResponse\"#\x92\xb5\x18\x1f\b\x05\x10\x02\x18\x01\"\x0emanage_secrets*\asite_id\x12w\n" + + "\x10DeleteSiteSecret\x12\".libops.v1.DeleteSiteSecretRequest\x1a\x16.google.protobuf.Empty\"'\x92\xb5\x18\x1f\b\x05\x10\x02\x18\x01\"\x0emanage_secrets*\asite_id\x98\xb5\x18\x01\x1a\x04\xa0\xb5\x18\x01B\x8e\x01\n" + "\rcom.libops.v1B\fSecretsProtoP\x01Z*github.com/libops/proto/libops/v1;libopsv1\xa2\x02\x03LXX\xaa\x02\tLibops.V1\xca\x02\tLibops\\V1\xe2\x02\x15Libops\\V1\\GPBMetadata\xea\x02\n" + "Libops::V1b\x06proto3" diff --git a/libops/v1/secrets.proto b/libops/v1/secrets.proto index 77d506d..bbe73b2 100644 --- a/libops/v1/secrets.proto +++ b/libops/v1/secrets.proto @@ -2,12 +2,12 @@ syntax = "proto3"; package libops.v1; -import "google/protobuf/descriptor.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/field_mask.proto"; import "libops/v1/options/assistant.proto"; import "libops/v1/options/audit.proto"; import "libops/v1/options/scope.proto"; +import "libops/v1/options/visibility.proto"; import "libops/v1/common/types.proto"; option go_package = "github.com/libops/proto/libops/v1;libopsv1"; @@ -18,9 +18,10 @@ option go_package = "github.com/libops/proto/libops/v1;libopsv1"; // OrganizationSecretService manages organization-level secrets service OrganizationSecretService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_PUBLIC; + // Create an organization secret rpc CreateOrganizationSecret(CreateOrganizationSecretRequest) returns (CreateOrganizationSecretResponse) { - option (libops.v1.options.assistant_playground) = true; option (libops.v1.options.required_scope) = { resource: RESOURCE_TYPE_ORGANIZATION level: ACCESS_LEVEL_WRITE @@ -54,7 +55,6 @@ service OrganizationSecretService { // Update an organization secret rpc UpdateOrganizationSecret(UpdateOrganizationSecretRequest) returns (UpdateOrganizationSecretResponse) { - option (libops.v1.options.assistant_playground) = true; option (libops.v1.options.required_scope) = { resource: RESOURCE_TYPE_ORGANIZATION level: ACCESS_LEVEL_WRITE @@ -77,9 +77,10 @@ service OrganizationSecretService { // ProjectSecretService manages project-level secrets service ProjectSecretService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_PUBLIC; + // Create a project secret rpc CreateProjectSecret(CreateProjectSecretRequest) returns (CreateProjectSecretResponse) { - option (libops.v1.options.assistant_playground) = true; option (libops.v1.options.required_scope) = { resource: RESOURCE_TYPE_PROJECT level: ACCESS_LEVEL_WRITE @@ -113,7 +114,6 @@ service ProjectSecretService { // Update a project secret rpc UpdateProjectSecret(UpdateProjectSecretRequest) returns (UpdateProjectSecretResponse) { - option (libops.v1.options.assistant_playground) = true; option (libops.v1.options.required_scope) = { resource: RESOURCE_TYPE_PROJECT level: ACCESS_LEVEL_WRITE @@ -136,9 +136,10 @@ service ProjectSecretService { // SiteSecretService manages site-level secrets service SiteSecretService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_PUBLIC; + // Create a site secret rpc CreateSiteSecret(CreateSiteSecretRequest) returns (CreateSiteSecretResponse) { - option (libops.v1.options.assistant_playground) = true; option (libops.v1.options.required_scope) = { resource: RESOURCE_TYPE_SITE level: ACCESS_LEVEL_WRITE @@ -172,7 +173,6 @@ service SiteSecretService { // Update a site secret rpc UpdateSiteSecret(UpdateSiteSecretRequest) returns (UpdateSiteSecretResponse) { - option (libops.v1.options.assistant_playground) = true; option (libops.v1.options.required_scope) = { resource: RESOURCE_TYPE_SITE level: ACCESS_LEVEL_WRITE diff --git a/libops/v1/settings.pb.go b/libops/v1/settings.pb.go index f7dcb36..236d48a 100644 --- a/libops/v1/settings.pb.go +++ b/libops/v1/settings.pb.go @@ -1781,7 +1781,7 @@ var File_libops_v1_settings_proto protoreflect.FileDescriptor const file_libops_v1_settings_proto_rawDesc = "" + "\n" + - "\x18libops/v1/settings.proto\x12\tlibops.v1\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\x1a!libops/v1/options/assistant.proto\x1a\x1dlibops/v1/options/audit.proto\x1a\x1dlibops/v1/options/scope.proto\x1a\x1clibops/v1/common/types.proto\"\xf5\x01\n" + + "\x18libops/v1/settings.proto\x12\tlibops.v1\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\x1a!libops/v1/options/assistant.proto\x1a\x1dlibops/v1/options/scope.proto\x1a\"libops/v1/options/visibility.proto\x1a\x1clibops/v1/common/types.proto\"\xf5\x01\n" + "\x13OrganizationSetting\x12\x1d\n" + "\n" + "setting_id\x18\x01 \x01(\tR\tsettingId\x12'\n" + @@ -1922,13 +1922,13 @@ const file_libops_v1_settings_proto_rawDesc = "" + "\x18DeleteSiteSettingRequest\x12\x17\n" + "\asite_id\x18\x01 \x01(\tR\x06siteId\x12\x1d\n" + "\n" + - "setting_id\x18\x02 \x01(\tR\tsettingId2\xcd\x06\n" + + "setting_id\x18\x02 \x01(\tR\tsettingId2\xd3\x06\n" + "\x1aOrganizationSettingService\x12\xaa\x01\n" + "\x19CreateOrganizationSetting\x12+.libops.v1.CreateOrganizationSettingRequest\x1a,.libops.v1.CreateOrganizationSettingResponse\"2\x92\xb5\x18*\b\x03\x10\x02\x18\x01\"\x0fmanage_settings2\x0forganization_id8\x03\x98\xb5\x18\x01\x12\x9c\x01\n" + "\x16GetOrganizationSetting\x12(.libops.v1.GetOrganizationSettingRequest\x1a).libops.v1.GetOrganizationSettingResponse\"-\x92\xb5\x18&\b\x03\x10\x01\x18\x01\"\rread_settings*\x0forganization_id\x90\x02\x01\x12\xa2\x01\n" + "\x18ListOrganizationSettings\x12*.libops.v1.ListOrganizationSettingsRequest\x1a+.libops.v1.ListOrganizationSettingsResponse\"-\x92\xb5\x18&\b\x03\x10\x01\x18\x01\"\rread_settings*\x0forganization_id\x90\x02\x01\x12\xa8\x01\n" + "\x19UpdateOrganizationSetting\x12+.libops.v1.UpdateOrganizationSettingRequest\x1a,.libops.v1.UpdateOrganizationSettingResponse\"0\x92\xb5\x18(\b\x03\x10\x02\x18\x01\"\x0fmanage_settings*\x0forganization_id\x98\xb5\x18\x01\x12\x92\x01\n" + - "\x19DeleteOrganizationSetting\x12+.libops.v1.DeleteOrganizationSettingRequest\x1a\x16.google.protobuf.Empty\"0\x92\xb5\x18(\b\x03\x10\x02\x18\x01\"\x0fmanage_settings*\x0forganization_id\x98\xb5\x18\x012\xe9\x05\n" + + "\x19DeleteOrganizationSetting\x12+.libops.v1.DeleteOrganizationSettingRequest\x1a\x16.google.protobuf.Empty\"0\x92\xb5\x18(\b\x03\x10\x02\x18\x01\"\x0fmanage_settings*\x0forganization_id\x98\xb5\x18\x01\x1a\x04\xa0\xb5\x18\x012\xef\x05\n" + "\x15ProjectSettingService\x12\x96\x01\n" + "\x14CreateProjectSetting\x12&.libops.v1.CreateProjectSettingRequest\x1a'.libops.v1.CreateProjectSettingResponse\"-\x92\xb5\x18%\b\x04\x10\x02\x18\x01\"\x0fmanage_settings2\n" + "project_id8\x04\x98\xb5\x18\x01\x12\x88\x01\n" + @@ -1939,13 +1939,13 @@ const file_libops_v1_settings_proto_rawDesc = "" + "\x14UpdateProjectSetting\x12&.libops.v1.UpdateProjectSettingRequest\x1a'.libops.v1.UpdateProjectSettingResponse\"+\x92\xb5\x18#\b\x04\x10\x02\x18\x01\"\x0fmanage_settings*\n" + "project_id\x98\xb5\x18\x01\x12\x83\x01\n" + "\x14DeleteProjectSetting\x12&.libops.v1.DeleteProjectSettingRequest\x1a\x16.google.protobuf.Empty\"+\x92\xb5\x18#\b\x04\x10\x02\x18\x01\"\x0fmanage_settings*\n" + - "project_id\x98\xb5\x18\x012\xab\x05\n" + + "project_id\x98\xb5\x18\x01\x1a\x04\xa0\xb5\x18\x012\xb1\x05\n" + "\x12SiteSettingService\x12\x8a\x01\n" + "\x11CreateSiteSetting\x12#.libops.v1.CreateSiteSettingRequest\x1a$.libops.v1.CreateSiteSettingResponse\"*\x92\xb5\x18\"\b\x05\x10\x02\x18\x01\"\x0fmanage_settings2\asite_id8\x05\x98\xb5\x18\x01\x12|\n" + "\x0eGetSiteSetting\x12 .libops.v1.GetSiteSettingRequest\x1a!.libops.v1.GetSiteSettingResponse\"%\x92\xb5\x18\x1e\b\x05\x10\x01\x18\x01\"\rread_settings*\asite_id\x90\x02\x01\x12\x82\x01\n" + "\x10ListSiteSettings\x12\".libops.v1.ListSiteSettingsRequest\x1a#.libops.v1.ListSiteSettingsResponse\"%\x92\xb5\x18\x1e\b\x05\x10\x01\x18\x01\"\rread_settings*\asite_id\x90\x02\x01\x12\x88\x01\n" + "\x11UpdateSiteSetting\x12#.libops.v1.UpdateSiteSettingRequest\x1a$.libops.v1.UpdateSiteSettingResponse\"(\x92\xb5\x18 \b\x05\x10\x02\x18\x01\"\x0fmanage_settings*\asite_id\x98\xb5\x18\x01\x12z\n" + - "\x11DeleteSiteSetting\x12#.libops.v1.DeleteSiteSettingRequest\x1a\x16.google.protobuf.Empty\"(\x92\xb5\x18 \b\x05\x10\x02\x18\x01\"\x0fmanage_settings*\asite_id\x98\xb5\x18\x01B\x8f\x01\n" + + "\x11DeleteSiteSetting\x12#.libops.v1.DeleteSiteSettingRequest\x1a\x16.google.protobuf.Empty\"(\x92\xb5\x18 \b\x05\x10\x02\x18\x01\"\x0fmanage_settings*\asite_id\x98\xb5\x18\x01\x1a\x04\xa0\xb5\x18\x01B\x8f\x01\n" + "\rcom.libops.v1B\rSettingsProtoP\x01Z*github.com/libops/proto/libops/v1;libopsv1\xa2\x02\x03LXX\xaa\x02\tLibops.V1\xca\x02\tLibops\\V1\xe2\x02\x15Libops\\V1\\GPBMetadata\xea\x02\n" + "Libops::V1b\x06proto3" diff --git a/libops/v1/settings.proto b/libops/v1/settings.proto index de7bd19..59f419f 100644 --- a/libops/v1/settings.proto +++ b/libops/v1/settings.proto @@ -5,8 +5,8 @@ package libops.v1; import "google/protobuf/empty.proto"; import "google/protobuf/field_mask.proto"; import "libops/v1/options/assistant.proto"; -import "libops/v1/options/audit.proto"; import "libops/v1/options/scope.proto"; +import "libops/v1/options/visibility.proto"; import "libops/v1/common/types.proto"; option go_package = "github.com/libops/proto/libops/v1;libopsv1"; @@ -17,6 +17,8 @@ option go_package = "github.com/libops/proto/libops/v1;libopsv1"; // OrganizationSettingService manages organization-level settings service OrganizationSettingService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_PUBLIC; + // Create an organization setting rpc CreateOrganizationSetting(CreateOrganizationSettingRequest) returns (CreateOrganizationSettingResponse) { option (libops.v1.options.assistant_playground) = true; @@ -76,6 +78,8 @@ service OrganizationSettingService { // ProjectSettingService manages project-level settings service ProjectSettingService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_PUBLIC; + // Create a project setting rpc CreateProjectSetting(CreateProjectSettingRequest) returns (CreateProjectSettingResponse) { option (libops.v1.options.assistant_playground) = true; @@ -135,6 +139,8 @@ service ProjectSettingService { // SiteSettingService manages site-level settings service SiteSettingService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_PUBLIC; + // Create a site setting rpc CreateSiteSetting(CreateSiteSettingRequest) returns (CreateSiteSettingResponse) { option (libops.v1.options.assistant_playground) = true; diff --git a/libops/v1/task_api.pb.go b/libops/v1/task_api.pb.go index b138c59..6e22e8b 100644 --- a/libops/v1/task_api.pb.go +++ b/libops/v1/task_api.pb.go @@ -1526,7 +1526,7 @@ var File_libops_v1_task_api_proto protoreflect.FileDescriptor const file_libops_v1_task_api_proto_rawDesc = "" + "\n" + - "\x18libops/v1/task_api.proto\x12\tlibops.v1\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1dlibops/v1/options/scope.proto\"\xb1\x01\n" + + "\x18libops/v1/task_api.proto\x12\tlibops.v1\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1dlibops/v1/options/scope.proto\x1a\"libops/v1/options/visibility.proto\"\xb1\x01\n" + "\fTaskLogEntry\x128\n" + "\ttimestamp\x18\x01 \x01(\v2\x1a.google.protobuf.TimestampR\ttimestamp\x12\x14\n" + "\x05level\x18\x02 \x01(\tR\x05level\x12\x18\n" + @@ -1665,7 +1665,7 @@ const file_libops_v1_task_api_proto_rawDesc = "" + "\x13TASK_HARNESS_CLAUDE\x10\x02\x12\x13\n" + "\x0fTASK_HARNESS_PI\x10\x03\x12\x19\n" + "\x15TASK_HARNESS_OPENCODE\x10\x04\x12\x17\n" + - "\x13TASK_HARNESS_GEMINI\x10\x052\xf7\x05\n" + + "\x13TASK_HARNESS_GEMINI\x10\x052\x81\x06\n" + "\vTaskService\x12z\n" + "\n" + "CreateTask\x12\x1c.libops.v1.CreateTaskRequest\x1a\x1d.libops.v1.CreateTaskResponse\"/\x92\xb5\x18+\b\x03\x10\x02\x18\x01\"\x12write:organization*\x0forganization_id\x12s\n" + @@ -1674,8 +1674,8 @@ const file_libops_v1_task_api_proto_rawDesc = "" + "\n" + "UpdateTask\x12\x1c.libops.v1.UpdateTaskRequest\x1a\x1d.libops.v1.UpdateTaskResponse\"/\x92\xb5\x18+\b\x03\x10\x02\x18\x01\"\x12write:organization*\x0forganization_id\x12z\n" + "\n" + - "CancelTask\x12\x1c.libops.v1.CancelTaskRequest\x1a\x1d.libops.v1.CancelTaskResponse\"/\x92\xb5\x18+\b\x03\x10\x02\x18\x01\"\x12write:organization*\x0forganization_id\x12\x83\x01\n" + - "\rAppendTaskLog\x12\x1f.libops.v1.AppendTaskLogRequest\x1a .libops.v1.AppendTaskLogResponse\"/\x92\xb5\x18+\b\x03\x10\x02\x18\x01\"\x12write:organization*\x0forganization_idB\x8e\x01\n" + + "CancelTask\x12\x1c.libops.v1.CancelTaskRequest\x1a\x1d.libops.v1.CancelTaskResponse\"/\x92\xb5\x18+\b\x03\x10\x02\x18\x01\"\x12write:organization*\x0forganization_id\x12\x87\x01\n" + + "\rAppendTaskLog\x12\x1f.libops.v1.AppendTaskLogRequest\x1a .libops.v1.AppendTaskLogResponse\"3\x92\xb5\x18+\b\x03\x10\x02\x18\x01\"\x12write:organization*\x0forganization_id\xa8\xb5\x18\x02\x1a\x04\xa0\xb5\x18\x01B\x8e\x01\n" + "\rcom.libops.v1B\fTaskApiProtoP\x01Z*github.com/libops/proto/libops/v1;libopsv1\xa2\x02\x03LXX\xaa\x02\tLibops.V1\xca\x02\tLibops\\V1\xe2\x02\x15Libops\\V1\\GPBMetadata\xea\x02\n" + "Libops::V1b\x06proto3" diff --git a/libops/v1/task_api.proto b/libops/v1/task_api.proto index 897f96b..4d06881 100644 --- a/libops/v1/task_api.proto +++ b/libops/v1/task_api.proto @@ -5,11 +5,14 @@ package libops.v1; import "google/protobuf/timestamp.proto"; import "google/protobuf/struct.proto"; import "libops/v1/options/scope.proto"; +import "libops/v1/options/visibility.proto"; option go_package = "github.com/libops/proto/libops/v1;libopsv1"; // TaskService manages queued automation tasks. service TaskService { + option (libops.v1.options.service_api_visibility) = API_VISIBILITY_PUBLIC; + // Create a new task from a natural language prompt. rpc CreateTask(CreateTaskRequest) returns (CreateTaskResponse) { option (libops.v1.options.required_scope) = { @@ -69,6 +72,7 @@ service TaskService { // Append a structured log entry to a task. rpc AppendTaskLog(AppendTaskLogRequest) returns (AppendTaskLogResponse) { + option (libops.v1.options.method_api_visibility) = API_VISIBILITY_INTERNAL; option (libops.v1.options.required_scope) = { resource: RESOURCE_TYPE_ORGANIZATION level: ACCESS_LEVEL_WRITE diff --git a/openapi-base.yaml b/openapi-base.yaml deleted file mode 100644 index 15cf566..0000000 --- a/openapi-base.yaml +++ /dev/null @@ -1,14 +0,0 @@ -openapi: 3.1.2 -info: - title: LibOps API - version: 1.0.0 - description: LibOps control plane API to run docker compose projects on Google Cloud - contact: - name: LibOps - url: https://github.com/libops/api - license: - name: MIT - url: https://github.com/libops/api/blob/main/LICENSE -servers: - - url: https://api.libops.io - description: Production server