diff --git a/README.md b/README.md index a0c69916..b2c6e609 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ runtime, records and the org-directory page. is refused by name (this week's `modelservice` typo); a check refuses another audience and a tampered token; an uninstall refuses an outstanding token on the next check; an installation acts for itself within its role; a pending - installation is issued nothing; a workload is issued its binding's headless + installation is issued no ordinary capability; a workload is issued its binding's headless scopes and refused an undeclared binding, a binding addressed elsewhere, a widened scope, a forged credential; a child hop attenuates and names its actor; `Keys` lets an independent core verifier check what was issued; TTL clamps. @@ -1246,3 +1246,117 @@ The edge serves it to the browser on the Setup tab's Connect face. The shared suite proves it in `RunSolutionPrincipalTarget` (`held_installations.go`). The edge accepts installation `<0.5.0`; a host must serve `ListHeld` and pass that case before a consumer relies on it. + +### Pending installation setup delivery + +The shared Go owner exposes `IssueInstallationStep(ctx, InstallationStepAttempt, +remainingLease)` for a **trusted host queue claimant**, and +`VerifyInstallationStep(ctx, token, audience)` for setup admission. This does not +change ordinary pending-installation refusal. The attempt pins the canonical +installation ID, organization, target name, revision and consent receipt, plus +the original reviewed delivery's principal (`TargetID`), generation and digest. +A host release-table target UUID is a different identity and remains part of the +host's immutable target/release/binding check. The claimant persists that original +association and receipt, never a token, and checks its job lease before minting. + +The credential uses Core's existing signer/verifier and a private pending-only +seal source. Its only scope is `installations:install` on that installation ID. +An opaque operation binding covers the exact attempt. No agent-role data scopes +or person/team grants are added. Normal `Verify`, `Check`, child exchange and +issuance cannot consume it, including after activation at the same revision. +Expiry is at most one minute, the configured maximum, and the supplied remaining +lease. The verifier adds no clock-skew extension to this window. Delivery is +idempotent while the attempt remains pending; the host still owns live queue +lease validation, retry and completion fencing. A lease cancellation is not +represented as an Authority revocation and must not be claimed as one. + +The existing `Check` transport reaches this verifier only with the exact method +`POST /_codefly/installations` or +`/host.installation.v1.InstallableService/Install`. These reserved setup methods +do not admit an ordinary capability, and advisory mode cannot relax their +credential checks. Receiver code supplies its own delivered audience and actual +method. Before invoking a configured callback or returning a cached ACK, it +compares the verified organization, installation ID and revision against strict +JSON `{installation_id, organization_id, revision}` and both installation +headers. An absent callback refuses; a solution may explicitly provide a no-op. +The owner library alone does not supply that runtime receiver or the host job +adapter; both require integration qualification. + +This is a method-policy compatibility change without a protobuf field or RPC +change: an older setup sender's ordinary agent capability is not accepted at +either reserved method. Hosts must adopt the dedicated pending-step issuer and +receivers must enforce the setup check before enabling canonical installation +delivery. There is no fallback to an ordinary token, delivered method policy, +or advisory acceptance when either side has not adopted the setup contract. + +`InstallStep` enqueues idempotently and returns failure/pending until that exact +job acknowledges. Enqueue success is never setup success. Only `Install`'s final +authorization, consent, generation and revision transaction activates canonical +state; no queue trigger activates it or a public installation mirror. Revocation, +replacement approval, redelivery and activation invalidate outstanding setup +credentials. The final state read is admission, not an atomic reservation through +callback I/O; a racing state change still prevents final installation activation. +Module installations and existing fixture-only trusted setup remain unchanged. + + +### Declared installation authority + +The signed authority document and shared Go delivery/offer types carry +`InstallationRole` and `EdgeDependencies{Module, Presence}`. Core's module parser +validates the declarations; subsequent carriers preserve the exact strings and +list order without trimming, defaults or inferred module identities. Edge +presence (`required` or `optional`) grants nothing. Each `Module` is the exact +signed `domain.Module.Principal`. The host resolves it and binding audiences +through the existing catalogue to live target UUIDs and bindings, then reviews +and fences those delivered prerequisites. A principal is never assumed to be a +target UUID; route aliases, binding IDs and release strings are not fallbacks. +This list is not effective access or a substitute for per-module consent. +Nil and empty dependency lists mean the same declaration. Absent fields are +omitted from `domain.Module` JSON, preserving the old review digest. Changes to +nonempty fields change the consent digest and require a fresh review. + +`Install` uses the declaration as follows: an absent selector retains explicit +caller selection; a named selector selects that declared role, accepting an empty +request or the same role; literal `none` accepts only an empty role request and +creates no standing authority. A caller may never submit `none` as a role. A +conflicting caller role refuses. Canonical `Installation.AgentRole` retains +`none` as a no-standing-authority marker, not a role or grant row. The installation +still has its stable principal and can use pending-only setup delivery. Ordinary +installation issuance refuses, and neither installer nor any person/team receives +an automatic grant. Existing installer-role and nonempty-role rules are unchanged. + +Hosts persist the original request separately from the effective machine role: +retry the original empty role request, never the stored `none` marker. Consent +intent, pending retry, revocation and final acknowledgement fences still apply. +This does not implement active installation re-consent or migrate existing active +installations to a new role selector. + +The new fields and reserved setup-method comments are exported through maintained +Codefly generation into local bindings, contracts and Go/Python/TypeScript clients. +The local document and offer adapters carry them. Tests exercise real signatures +on both generated delivery messages, the authenticated reader and persistence, +including disagreement refusal before storage. CLI producer construction and +Accounts queue/receiver integration still require their own qualification; these +local tests do not establish a deployed installation. + + +The two signed delivery carriers must agree on these declarations. Shared +`delivered.Pair` and `delivered.Module` refuse a different selector or a different +ordered `(Module, Presence)` dependency list; neither carrier takes precedence. +Empty and `none` differ, while nil and empty edge lists compare equally. A new +nonempty declaration present on only one carrier refuses, including an absent +sidecar. Legacy delivery with both declarations absent remains supported. +This compares two projections of one authored value; it adds no declaration +parser, sorting, trimming or defaulting. `FromCore` alone is a projection, not +paired-delivery admission. Hosts use the shared paired reader before persistence. +The CLI must fill both generated carriers from the same resolved declaration, +and each host must adopt its wire adapters before complete delivery is enabled. + +The generation baseline used released Codefly 0.1.221 and proto companion 0.0.18: +unchanged pre-feature proto inputs reproduced their tracked outputs exactly. +Repeated generation of the new proto and all three clients is byte-identical, +and `codefly generate contracts authority --check` passes. Client generation +selects `--module-name api` explicitly to preserve the existing root Go package +when consuming the exported contracts directory. Only existing reviewed entries +in the generated-file inventory have their hashes refreshed; no exclusion is +added. SQL/cell installation evidence remains owned by the integrating host. diff --git a/libraries/authority-client/contract/catalog.codefly.json b/libraries/authority-client/contract/catalog.codefly.json index d3944552..431b6173 100644 --- a/libraries/authority-client/contract/catalog.codefly.json +++ b/libraries/authority-client/contract/catalog.codefly.json @@ -1,6 +1,6 @@ { "schema": "codefly/module-api-contracts/v1", - "package": "local", + "package": "codefly/authority", "version": "0.1.0", "endpoints": [ { @@ -8,9 +8,9 @@ "endpoint": "grpc", "api": "grpc", "kind": "protobuf", - "package": "api", + "package": "", "path": "contract.binpb", - "digest": "sha256:2d74a1ad05ecab544606e2e52630cc5cb6c16729371f091262dfb45a686d2712", + "digest": "sha256:b49a5399e647c61b1991fc22bf7fcf8262ab17333dbe01b4afd1d16c346f3ea7", "services": [ { "name": "LocalService", @@ -18,6 +18,70 @@ "procedures": [ "/api.LocalService/Version" ] + }, + { + "name": "ArtifactService", + "fullName": "host.artifact.v1.ArtifactService", + "procedures": [ + "/host.artifact.v1.ArtifactService/Approve", + "/host.artifact.v1.ArtifactService/Authorize", + "/host.artifact.v1.ArtifactService/Revoke" + ] + }, + { + "name": "AuthorityService", + "fullName": "host.authority.v1.AuthorityService", + "procedures": [ + "/host.authority.v1.AuthorityService/AttachResource", + "/host.authority.v1.AuthorityService/AuthorizeTransfer", + "/host.authority.v1.AuthorityService/Check", + "/host.authority.v1.AuthorityService/Delegate", + "/host.authority.v1.AuthorityService/DetachResource", + "/host.authority.v1.AuthorityService/GetDelegation", + "/host.authority.v1.AuthorityService/Issue", + "/host.authority.v1.AuthorityService/Keys", + "/host.authority.v1.AuthorityService/ListResources", + "/host.authority.v1.AuthorityService/RegisterAdmissionKey", + "/host.authority.v1.AuthorityService/RevokeDelegation" + ] + }, + { + "name": "CompositionService", + "fullName": "host.composition.v1.CompositionService", + "procedures": [ + "/host.composition.v1.CompositionService/ApplyManifest" + ] + }, + { + "name": "InstallationService", + "fullName": "host.installation.v1.InstallationService", + "procedures": [ + "/host.installation.v1.InstallationService/Current", + "/host.installation.v1.InstallationService/GrantRole", + "/host.installation.v1.InstallationService/Install", + "/host.installation.v1.InstallationService/List", + "/host.installation.v1.InstallationService/ListHeld", + "/host.installation.v1.InstallationService/ListRoleGrants", + "/host.installation.v1.InstallationService/Review", + "/host.installation.v1.InstallationService/RevokeRole" + ] + }, + { + "name": "OfferService", + "fullName": "host.installation.v1.OfferService", + "procedures": [ + "/host.installation.v1.OfferService/ListOffers", + "/host.installation.v1.OfferService/ReviewOffer" + ] + }, + { + "name": "SessionService", + "fullName": "host.session.v1.SessionService", + "procedures": [ + "/host.session.v1.SessionService/Current", + "/host.session.v1.SessionService/ListFixturePeople", + "/host.session.v1.SessionService/SignIn" + ] } ] } diff --git a/libraries/authority-client/contract/contract.binpb b/libraries/authority-client/contract/contract.binpb index 9807338a..236b452b 100644 Binary files a/libraries/authority-client/contract/contract.binpb and b/libraries/authority-client/contract/contract.binpb differ diff --git a/libraries/authority-client/go/gen/host/authority/v1/authority.pb.go b/libraries/authority-client/go/gen/host/authority/v1/authority.pb.go index cd7852dc..1acc5e8a 100644 --- a/libraries/authority-client/go/gen/host/authority/v1/authority.pb.go +++ b/libraries/authority-client/go/gen/host/authority/v1/authority.pb.go @@ -827,6 +827,15 @@ type CheckRequest struct { // applies what the audience module's delivery declares for that method, and // refuses by name a call that does not hold it, a method that declares // nothing, and a method the module does not serve. + // Two exact methods are reserved for installation setup instead of delivered + // method policy: "POST /_codefly/installations" and + // "/host.installation.v1.InstallableService/Install". They accept only the + // setup-only credential for the exact current consented pending installation; + // ordinary capabilities are refused, even if delivered policy permits them. + // The receiver supplies its actual method and its own delivered audience, + // never values selected by the request body. The verified organization and + // installation seal must match the setup body and carriers before callback + // execution or a cached acknowledgement. Setup admits no ordinary data work. Method string `protobuf:"bytes,3,opt,name=method,proto3" json:"method,omitempty"` // Advisory asks for the policy decision without its refusal: the capability // is still verified and refused when it is not sound, but what the method @@ -834,6 +843,8 @@ type CheckRequest struct { // capability is answered. A hop in a composition that runs permissive // (hop/ENFORCEMENT=warn) asks this way and logs the refusal it did not make. // Hosts may refuse advisory outside local development runs. + // At either reserved setup method, advisory does not relax setup credential + // authentication or its pending-attempt checks: a refusal is always an error. Advisory bool `protobuf:"varint,4,opt,name=advisory,proto3" json:"advisory,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache diff --git a/libraries/authority-client/go/gen/host/composition/v1/composition.pb.go b/libraries/authority-client/go/gen/host/composition/v1/composition.pb.go index e2001f11..4a9d9a7c 100644 --- a/libraries/authority-client/go/gen/host/composition/v1/composition.pb.go +++ b/libraries/authority-client/go/gen/host/composition/v1/composition.pb.go @@ -141,9 +141,14 @@ type AuthorityManifestV1 struct { // (Claims.AllowsOwned): only an audience declaring it receives one, and a // delivery withdrawing it refuses the owner-selected capabilities issued // before. Every solution receiving its roles' scopes declares it. - OwnerAware bool `protobuf:"varint,7,opt,name=owner_aware,json=ownerAware,proto3" json:"owner_aware,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + OwnerAware bool `protobuf:"varint,7,opt,name=owner_aware,json=ownerAware,proto3" json:"owner_aware,omitempty"` + // Core-validated selector, carried verbatim: empty is legacy caller selection, + // "none" declares no standing machine authority, otherwise a declared role. + InstallationRole string `protobuf:"bytes,8,opt,name=installation_role,json=installationRole,proto3" json:"installation_role,omitempty"` + // Viewer-edge presence only; grants no authority. Nil and empty are equivalent. + EdgeDependencies []*EdgeDependencyV1 `protobuf:"bytes,9,rep,name=edge_dependencies,json=edgeDependencies,proto3" json:"edge_dependencies,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *AuthorityManifestV1) Reset() { @@ -225,6 +230,75 @@ func (x *AuthorityManifestV1) GetOwnerAware() bool { return false } +func (x *AuthorityManifestV1) GetInstallationRole() string { + if x != nil { + return x.InstallationRole + } + return "" +} + +func (x *AuthorityManifestV1) GetEdgeDependencies() []*EdgeDependencyV1 { + if x != nil { + return x.EdgeDependencies + } + return nil +} + +// EdgeDependencyV1 names the exact signed module principal. The host resolves +// it to a live catalogue target; it is not a target UUID, route alias or binding id. +type EdgeDependencyV1 struct { + state protoimpl.MessageState `protogen:"open.v1"` + Module string `protobuf:"bytes,1,opt,name=module,proto3" json:"module,omitempty"` + // Core validates required or optional; delivery does not default this value. + Presence string `protobuf:"bytes,2,opt,name=presence,proto3" json:"presence,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *EdgeDependencyV1) Reset() { + *x = EdgeDependencyV1{} + mi := &file_host_composition_v1_composition_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *EdgeDependencyV1) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EdgeDependencyV1) ProtoMessage() {} + +func (x *EdgeDependencyV1) ProtoReflect() protoreflect.Message { + mi := &file_host_composition_v1_composition_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EdgeDependencyV1.ProtoReflect.Descriptor instead. +func (*EdgeDependencyV1) Descriptor() ([]byte, []int) { + return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{2} +} + +func (x *EdgeDependencyV1) GetModule() string { + if x != nil { + return x.Module + } + return "" +} + +func (x *EdgeDependencyV1) GetPresence() string { + if x != nil { + return x.Presence + } + return "" +} + // MethodPolicyV1 is what a call to one method must hold, as the module's // contract declares it with the authority.hop.v1.policy option. type MethodPolicyV1 struct { @@ -243,7 +317,7 @@ type MethodPolicyV1 struct { func (x *MethodPolicyV1) Reset() { *x = MethodPolicyV1{} - mi := &file_host_composition_v1_composition_proto_msgTypes[2] + mi := &file_host_composition_v1_composition_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -255,7 +329,7 @@ func (x *MethodPolicyV1) String() string { func (*MethodPolicyV1) ProtoMessage() {} func (x *MethodPolicyV1) ProtoReflect() protoreflect.Message { - mi := &file_host_composition_v1_composition_proto_msgTypes[2] + mi := &file_host_composition_v1_composition_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -268,7 +342,7 @@ func (x *MethodPolicyV1) ProtoReflect() protoreflect.Message { // Deprecated: Use MethodPolicyV1.ProtoReflect.Descriptor instead. func (*MethodPolicyV1) Descriptor() ([]byte, []int) { - return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{2} + return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{3} } func (x *MethodPolicyV1) GetMethod() string { @@ -304,7 +378,7 @@ type SignedManifestV1 struct { func (x *SignedManifestV1) Reset() { *x = SignedManifestV1{} - mi := &file_host_composition_v1_composition_proto_msgTypes[3] + mi := &file_host_composition_v1_composition_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -316,7 +390,7 @@ func (x *SignedManifestV1) String() string { func (*SignedManifestV1) ProtoMessage() {} func (x *SignedManifestV1) ProtoReflect() protoreflect.Message { - mi := &file_host_composition_v1_composition_proto_msgTypes[3] + mi := &file_host_composition_v1_composition_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -329,7 +403,7 @@ func (x *SignedManifestV1) ProtoReflect() protoreflect.Message { // Deprecated: Use SignedManifestV1.ProtoReflect.Descriptor instead. func (*SignedManifestV1) Descriptor() ([]byte, []int) { - return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{3} + return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{4} } func (x *SignedManifestV1) GetDocument() []byte { @@ -361,7 +435,7 @@ type ApplyManifestResponse struct { func (x *ApplyManifestResponse) Reset() { *x = ApplyManifestResponse{} - mi := &file_host_composition_v1_composition_proto_msgTypes[4] + mi := &file_host_composition_v1_composition_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -373,7 +447,7 @@ func (x *ApplyManifestResponse) String() string { func (*ApplyManifestResponse) ProtoMessage() {} func (x *ApplyManifestResponse) ProtoReflect() protoreflect.Message { - mi := &file_host_composition_v1_composition_proto_msgTypes[4] + mi := &file_host_composition_v1_composition_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -386,7 +460,7 @@ func (x *ApplyManifestResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ApplyManifestResponse.ProtoReflect.Descriptor instead. func (*ApplyManifestResponse) Descriptor() ([]byte, []int) { - return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{4} + return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{5} } func (x *ApplyManifestResponse) GetPrincipal() string { @@ -438,7 +512,7 @@ type ModuleManifestV1 struct { func (x *ModuleManifestV1) Reset() { *x = ModuleManifestV1{} - mi := &file_host_composition_v1_composition_proto_msgTypes[5] + mi := &file_host_composition_v1_composition_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -450,7 +524,7 @@ func (x *ModuleManifestV1) String() string { func (*ModuleManifestV1) ProtoMessage() {} func (x *ModuleManifestV1) ProtoReflect() protoreflect.Message { - mi := &file_host_composition_v1_composition_proto_msgTypes[5] + mi := &file_host_composition_v1_composition_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -463,7 +537,7 @@ func (x *ModuleManifestV1) ProtoReflect() protoreflect.Message { // Deprecated: Use ModuleManifestV1.ProtoReflect.Descriptor instead. func (*ModuleManifestV1) Descriptor() ([]byte, []int) { - return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{5} + return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{6} } func (x *ModuleManifestV1) GetModule() string { @@ -542,7 +616,7 @@ type RoleV1 struct { func (x *RoleV1) Reset() { *x = RoleV1{} - mi := &file_host_composition_v1_composition_proto_msgTypes[6] + mi := &file_host_composition_v1_composition_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -554,7 +628,7 @@ func (x *RoleV1) String() string { func (*RoleV1) ProtoMessage() {} func (x *RoleV1) ProtoReflect() protoreflect.Message { - mi := &file_host_composition_v1_composition_proto_msgTypes[6] + mi := &file_host_composition_v1_composition_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -567,7 +641,7 @@ func (x *RoleV1) ProtoReflect() protoreflect.Message { // Deprecated: Use RoleV1.ProtoReflect.Descriptor instead. func (*RoleV1) Descriptor() ([]byte, []int) { - return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{6} + return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{7} } func (x *RoleV1) GetName() string { @@ -613,7 +687,7 @@ type BindingV1 struct { func (x *BindingV1) Reset() { *x = BindingV1{} - mi := &file_host_composition_v1_composition_proto_msgTypes[7] + mi := &file_host_composition_v1_composition_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -625,7 +699,7 @@ func (x *BindingV1) String() string { func (*BindingV1) ProtoMessage() {} func (x *BindingV1) ProtoReflect() protoreflect.Message { - mi := &file_host_composition_v1_composition_proto_msgTypes[7] + mi := &file_host_composition_v1_composition_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -638,7 +712,7 @@ func (x *BindingV1) ProtoReflect() protoreflect.Message { // Deprecated: Use BindingV1.ProtoReflect.Descriptor instead. func (*BindingV1) Descriptor() ([]byte, []int) { - return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{7} + return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{8} } func (x *BindingV1) GetId() string { @@ -689,7 +763,7 @@ type ResourceKindV1 struct { func (x *ResourceKindV1) Reset() { *x = ResourceKindV1{} - mi := &file_host_composition_v1_composition_proto_msgTypes[8] + mi := &file_host_composition_v1_composition_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -701,7 +775,7 @@ func (x *ResourceKindV1) String() string { func (*ResourceKindV1) ProtoMessage() {} func (x *ResourceKindV1) ProtoReflect() protoreflect.Message { - mi := &file_host_composition_v1_composition_proto_msgTypes[8] + mi := &file_host_composition_v1_composition_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -714,7 +788,7 @@ func (x *ResourceKindV1) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceKindV1.ProtoReflect.Descriptor instead. func (*ResourceKindV1) Descriptor() ([]byte, []int) { - return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{8} + return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{9} } func (x *ResourceKindV1) GetKind() string { @@ -744,7 +818,7 @@ type PageV1 struct { func (x *PageV1) Reset() { *x = PageV1{} - mi := &file_host_composition_v1_composition_proto_msgTypes[9] + mi := &file_host_composition_v1_composition_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -756,7 +830,7 @@ func (x *PageV1) String() string { func (*PageV1) ProtoMessage() {} func (x *PageV1) ProtoReflect() protoreflect.Message { - mi := &file_host_composition_v1_composition_proto_msgTypes[9] + mi := &file_host_composition_v1_composition_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -769,7 +843,7 @@ func (x *PageV1) ProtoReflect() protoreflect.Message { // Deprecated: Use PageV1.ProtoReflect.Descriptor instead. func (*PageV1) Descriptor() ([]byte, []int) { - return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{9} + return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{10} } func (x *PageV1) GetTitle() string { @@ -797,7 +871,7 @@ const file_host_composition_v1_composition_proto_rawDesc = "" + "\n" + "credential\x18\x03 \x01(\tR\n" + "credential\x12C\n" + - "\tauthority\x18\x04 \x01(\v2%.host.composition.v1.SignedManifestV1R\tauthority\"\xd9\x02\n" + + "\tauthority\x18\x04 \x01(\v2%.host.composition.v1.SignedManifestV1R\tauthority\"\xda\x03\n" + "\x13AuthorityManifestV1\x12\x16\n" + "\x06module\x18\x01 \x01(\tR\x06module\x12\x1e\n" + "\n" + @@ -808,7 +882,12 @@ const file_host_composition_v1_composition_proto_rawDesc = "" + "\x0escope_ceilings\x18\x05 \x03(\v2\x1e.host.authority.v1.WorkScopeV1R\rscopeCeilings\x121\n" + "\x05roles\x18\x06 \x03(\v2\x1b.host.composition.v1.RoleV1R\x05roles\x12\x1f\n" + "\vowner_aware\x18\a \x01(\bR\n" + - "ownerAware\"\x92\x01\n" + + "ownerAware\x12+\n" + + "\x11installation_role\x18\b \x01(\tR\x10installationRole\x12R\n" + + "\x11edge_dependencies\x18\t \x03(\v2%.host.composition.v1.EdgeDependencyV1R\x10edgeDependencies\"F\n" + + "\x10EdgeDependencyV1\x12\x16\n" + + "\x06module\x18\x01 \x01(\tR\x06module\x12\x1a\n" + + "\bpresence\x18\x02 \x01(\tR\bpresence\"\x92\x01\n" + "\x0eMethodPolicyV1\x12\x16\n" + "\x06method\x18\x01 \x01(\tR\x06method\x12:\n" + "\brequires\x18\x02 \x03(\v2\x1e.host.authority.v1.WorkScopeV1R\brequires\x12,\n" + @@ -865,44 +944,46 @@ func file_host_composition_v1_composition_proto_rawDescGZIP() []byte { return file_host_composition_v1_composition_proto_rawDescData } -var file_host_composition_v1_composition_proto_msgTypes = make([]protoimpl.MessageInfo, 10) +var file_host_composition_v1_composition_proto_msgTypes = make([]protoimpl.MessageInfo, 11) var file_host_composition_v1_composition_proto_goTypes = []any{ (*ApplyManifestRequest)(nil), // 0: host.composition.v1.ApplyManifestRequest (*AuthorityManifestV1)(nil), // 1: host.composition.v1.AuthorityManifestV1 - (*MethodPolicyV1)(nil), // 2: host.composition.v1.MethodPolicyV1 - (*SignedManifestV1)(nil), // 3: host.composition.v1.SignedManifestV1 - (*ApplyManifestResponse)(nil), // 4: host.composition.v1.ApplyManifestResponse - (*ModuleManifestV1)(nil), // 5: host.composition.v1.ModuleManifestV1 - (*RoleV1)(nil), // 6: host.composition.v1.RoleV1 - (*BindingV1)(nil), // 7: host.composition.v1.BindingV1 - (*ResourceKindV1)(nil), // 8: host.composition.v1.ResourceKindV1 - (*PageV1)(nil), // 9: host.composition.v1.PageV1 - (*v1.WorkScopeV1)(nil), // 10: host.authority.v1.WorkScopeV1 + (*EdgeDependencyV1)(nil), // 2: host.composition.v1.EdgeDependencyV1 + (*MethodPolicyV1)(nil), // 3: host.composition.v1.MethodPolicyV1 + (*SignedManifestV1)(nil), // 4: host.composition.v1.SignedManifestV1 + (*ApplyManifestResponse)(nil), // 5: host.composition.v1.ApplyManifestResponse + (*ModuleManifestV1)(nil), // 6: host.composition.v1.ModuleManifestV1 + (*RoleV1)(nil), // 7: host.composition.v1.RoleV1 + (*BindingV1)(nil), // 8: host.composition.v1.BindingV1 + (*ResourceKindV1)(nil), // 9: host.composition.v1.ResourceKindV1 + (*PageV1)(nil), // 10: host.composition.v1.PageV1 + (*v1.WorkScopeV1)(nil), // 11: host.authority.v1.WorkScopeV1 } var file_host_composition_v1_composition_proto_depIdxs = []int32{ - 5, // 0: host.composition.v1.ApplyManifestRequest.manifest:type_name -> host.composition.v1.ModuleManifestV1 - 3, // 1: host.composition.v1.ApplyManifestRequest.carrier:type_name -> host.composition.v1.SignedManifestV1 - 3, // 2: host.composition.v1.ApplyManifestRequest.authority:type_name -> host.composition.v1.SignedManifestV1 - 2, // 3: host.composition.v1.AuthorityManifestV1.methods:type_name -> host.composition.v1.MethodPolicyV1 - 10, // 4: host.composition.v1.AuthorityManifestV1.scope_ceilings:type_name -> host.authority.v1.WorkScopeV1 - 6, // 5: host.composition.v1.AuthorityManifestV1.roles:type_name -> host.composition.v1.RoleV1 - 10, // 6: host.composition.v1.MethodPolicyV1.requires:type_name -> host.authority.v1.WorkScopeV1 - 7, // 7: host.composition.v1.ModuleManifestV1.bindings:type_name -> host.composition.v1.BindingV1 - 8, // 8: host.composition.v1.ModuleManifestV1.resource_kinds:type_name -> host.composition.v1.ResourceKindV1 - 9, // 9: host.composition.v1.ModuleManifestV1.page:type_name -> host.composition.v1.PageV1 - 6, // 10: host.composition.v1.ModuleManifestV1.roles:type_name -> host.composition.v1.RoleV1 - 10, // 11: host.composition.v1.RoleV1.scopes:type_name -> host.authority.v1.WorkScopeV1 - 10, // 12: host.composition.v1.RoleV1.installation_owned_scopes:type_name -> host.authority.v1.WorkScopeV1 - 10, // 13: host.composition.v1.BindingV1.invoke_scopes:type_name -> host.authority.v1.WorkScopeV1 - 10, // 14: host.composition.v1.BindingV1.headless_scopes:type_name -> host.authority.v1.WorkScopeV1 - 10, // 15: host.composition.v1.BindingV1.lookup_scopes:type_name -> host.authority.v1.WorkScopeV1 - 0, // 16: host.composition.v1.CompositionService.ApplyManifest:input_type -> host.composition.v1.ApplyManifestRequest - 4, // 17: host.composition.v1.CompositionService.ApplyManifest:output_type -> host.composition.v1.ApplyManifestResponse - 17, // [17:18] is the sub-list for method output_type - 16, // [16:17] is the sub-list for method input_type - 16, // [16:16] is the sub-list for extension type_name - 16, // [16:16] is the sub-list for extension extendee - 0, // [0:16] is the sub-list for field type_name + 6, // 0: host.composition.v1.ApplyManifestRequest.manifest:type_name -> host.composition.v1.ModuleManifestV1 + 4, // 1: host.composition.v1.ApplyManifestRequest.carrier:type_name -> host.composition.v1.SignedManifestV1 + 4, // 2: host.composition.v1.ApplyManifestRequest.authority:type_name -> host.composition.v1.SignedManifestV1 + 3, // 3: host.composition.v1.AuthorityManifestV1.methods:type_name -> host.composition.v1.MethodPolicyV1 + 11, // 4: host.composition.v1.AuthorityManifestV1.scope_ceilings:type_name -> host.authority.v1.WorkScopeV1 + 7, // 5: host.composition.v1.AuthorityManifestV1.roles:type_name -> host.composition.v1.RoleV1 + 2, // 6: host.composition.v1.AuthorityManifestV1.edge_dependencies:type_name -> host.composition.v1.EdgeDependencyV1 + 11, // 7: host.composition.v1.MethodPolicyV1.requires:type_name -> host.authority.v1.WorkScopeV1 + 8, // 8: host.composition.v1.ModuleManifestV1.bindings:type_name -> host.composition.v1.BindingV1 + 9, // 9: host.composition.v1.ModuleManifestV1.resource_kinds:type_name -> host.composition.v1.ResourceKindV1 + 10, // 10: host.composition.v1.ModuleManifestV1.page:type_name -> host.composition.v1.PageV1 + 7, // 11: host.composition.v1.ModuleManifestV1.roles:type_name -> host.composition.v1.RoleV1 + 11, // 12: host.composition.v1.RoleV1.scopes:type_name -> host.authority.v1.WorkScopeV1 + 11, // 13: host.composition.v1.RoleV1.installation_owned_scopes:type_name -> host.authority.v1.WorkScopeV1 + 11, // 14: host.composition.v1.BindingV1.invoke_scopes:type_name -> host.authority.v1.WorkScopeV1 + 11, // 15: host.composition.v1.BindingV1.headless_scopes:type_name -> host.authority.v1.WorkScopeV1 + 11, // 16: host.composition.v1.BindingV1.lookup_scopes:type_name -> host.authority.v1.WorkScopeV1 + 0, // 17: host.composition.v1.CompositionService.ApplyManifest:input_type -> host.composition.v1.ApplyManifestRequest + 5, // 18: host.composition.v1.CompositionService.ApplyManifest:output_type -> host.composition.v1.ApplyManifestResponse + 18, // [18:19] is the sub-list for method output_type + 17, // [17:18] is the sub-list for method input_type + 17, // [17:17] is the sub-list for extension type_name + 17, // [17:17] is the sub-list for extension extendee + 0, // [0:17] is the sub-list for field type_name } func init() { file_host_composition_v1_composition_proto_init() } @@ -916,7 +997,7 @@ func file_host_composition_v1_composition_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_host_composition_v1_composition_proto_rawDesc), len(file_host_composition_v1_composition_proto_rawDesc)), NumEnums: 0, - NumMessages: 10, + NumMessages: 11, NumExtensions: 0, NumServices: 1, }, diff --git a/libraries/authority-client/go/gen/host/installation/v1/installation.pb.go b/libraries/authority-client/go/gen/host/installation/v1/installation.pb.go index 14eb3d04..dcf393d2 100644 --- a/libraries/authority-client/go/gen/host/installation/v1/installation.pb.go +++ b/libraries/authority-client/go/gen/host/installation/v1/installation.pb.go @@ -1223,8 +1223,14 @@ type ReviewedOffer struct { // False when this run delivered no prepared Runnable bindings: no tool is // shown, which says nothing about what a composed binding would expose. ToolsComposed bool `protobuf:"varint,6,opt,name=tools_composed,json=toolsComposed,proto3" json:"tools_composed,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // Signed machine-role selector: empty is undeclared, "none" is no standing + // authority, otherwise a named role. This is not a person or team grant. + InstallationRole string `protobuf:"bytes,7,opt,name=installation_role,json=installationRole,proto3" json:"installation_role,omitempty"` + // Exact declared viewer-edge presence, covered by the reviewed digest. + // Required access and per-module approval are separate host review concerns. + EdgeDependencies []*OfferDependency `protobuf:"bytes,8,rep,name=edge_dependencies,json=edgeDependencies,proto3" json:"edge_dependencies,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *ReviewedOffer) Reset() { @@ -1299,6 +1305,74 @@ func (x *ReviewedOffer) GetToolsComposed() bool { return false } +func (x *ReviewedOffer) GetInstallationRole() string { + if x != nil { + return x.InstallationRole + } + return "" +} + +func (x *ReviewedOffer) GetEdgeDependencies() []*OfferDependency { + if x != nil { + return x.EdgeDependencies + } + return nil +} + +// OfferDependency carries presence only, never scopes or inferred binding ids. +// Module is the exact signed principal, resolved by the host to its live target. +type OfferDependency struct { + state protoimpl.MessageState `protogen:"open.v1"` + Module string `protobuf:"bytes,1,opt,name=module,proto3" json:"module,omitempty"` + Presence string `protobuf:"bytes,2,opt,name=presence,proto3" json:"presence,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *OfferDependency) Reset() { + *x = OfferDependency{} + mi := &file_host_installation_v1_installation_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *OfferDependency) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OfferDependency) ProtoMessage() {} + +func (x *OfferDependency) ProtoReflect() protoreflect.Message { + mi := &file_host_installation_v1_installation_proto_msgTypes[19] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use OfferDependency.ProtoReflect.Descriptor instead. +func (*OfferDependency) Descriptor() ([]byte, []int) { + return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{19} +} + +func (x *OfferDependency) GetModule() string { + if x != nil { + return x.Module + } + return "" +} + +func (x *OfferDependency) GetPresence() string { + if x != nil { + return x.Presence + } + return "" +} + // OfferBinding is one declared call from the solution to a module. type OfferBinding struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -1312,7 +1386,7 @@ type OfferBinding struct { func (x *OfferBinding) Reset() { *x = OfferBinding{} - mi := &file_host_installation_v1_installation_proto_msgTypes[19] + mi := &file_host_installation_v1_installation_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1324,7 +1398,7 @@ func (x *OfferBinding) String() string { func (*OfferBinding) ProtoMessage() {} func (x *OfferBinding) ProtoReflect() protoreflect.Message { - mi := &file_host_installation_v1_installation_proto_msgTypes[19] + mi := &file_host_installation_v1_installation_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1337,7 +1411,7 @@ func (x *OfferBinding) ProtoReflect() protoreflect.Message { // Deprecated: Use OfferBinding.ProtoReflect.Descriptor instead. func (*OfferBinding) Descriptor() ([]byte, []int) { - return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{19} + return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{20} } func (x *OfferBinding) GetBinding() string { @@ -1375,7 +1449,7 @@ type OfferRole struct { func (x *OfferRole) Reset() { *x = OfferRole{} - mi := &file_host_installation_v1_installation_proto_msgTypes[20] + mi := &file_host_installation_v1_installation_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1387,7 +1461,7 @@ func (x *OfferRole) String() string { func (*OfferRole) ProtoMessage() {} func (x *OfferRole) ProtoReflect() protoreflect.Message { - mi := &file_host_installation_v1_installation_proto_msgTypes[20] + mi := &file_host_installation_v1_installation_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1400,7 +1474,7 @@ func (x *OfferRole) ProtoReflect() protoreflect.Message { // Deprecated: Use OfferRole.ProtoReflect.Descriptor instead. func (*OfferRole) Descriptor() ([]byte, []int) { - return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{20} + return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{21} } func (x *OfferRole) GetName() string { @@ -1440,7 +1514,7 @@ type OfferScope struct { func (x *OfferScope) Reset() { *x = OfferScope{} - mi := &file_host_installation_v1_installation_proto_msgTypes[21] + mi := &file_host_installation_v1_installation_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1452,7 +1526,7 @@ func (x *OfferScope) String() string { func (*OfferScope) ProtoMessage() {} func (x *OfferScope) ProtoReflect() protoreflect.Message { - mi := &file_host_installation_v1_installation_proto_msgTypes[21] + mi := &file_host_installation_v1_installation_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1465,7 +1539,7 @@ func (x *OfferScope) ProtoReflect() protoreflect.Message { // Deprecated: Use OfferScope.ProtoReflect.Descriptor instead. func (*OfferScope) Descriptor() ([]byte, []int) { - return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{21} + return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{22} } func (x *OfferScope) GetResourceKind() string { @@ -1513,7 +1587,7 @@ type OfferCapability struct { func (x *OfferCapability) Reset() { *x = OfferCapability{} - mi := &file_host_installation_v1_installation_proto_msgTypes[22] + mi := &file_host_installation_v1_installation_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1525,7 +1599,7 @@ func (x *OfferCapability) String() string { func (*OfferCapability) ProtoMessage() {} func (x *OfferCapability) ProtoReflect() protoreflect.Message { - mi := &file_host_installation_v1_installation_proto_msgTypes[22] + mi := &file_host_installation_v1_installation_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1538,7 +1612,7 @@ func (x *OfferCapability) ProtoReflect() protoreflect.Message { // Deprecated: Use OfferCapability.ProtoReflect.Descriptor instead. func (*OfferCapability) Descriptor() ([]byte, []int) { - return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{22} + return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{23} } func (x *OfferCapability) GetModule() string { @@ -1584,7 +1658,7 @@ type OfferTool struct { func (x *OfferTool) Reset() { *x = OfferTool{} - mi := &file_host_installation_v1_installation_proto_msgTypes[23] + mi := &file_host_installation_v1_installation_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1596,7 +1670,7 @@ func (x *OfferTool) String() string { func (*OfferTool) ProtoMessage() {} func (x *OfferTool) ProtoReflect() protoreflect.Message { - mi := &file_host_installation_v1_installation_proto_msgTypes[23] + mi := &file_host_installation_v1_installation_proto_msgTypes[24] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1609,7 +1683,7 @@ func (x *OfferTool) ProtoReflect() protoreflect.Message { // Deprecated: Use OfferTool.ProtoReflect.Descriptor instead. func (*OfferTool) Descriptor() ([]byte, []int) { - return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{23} + return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{24} } func (x *OfferTool) GetName() string { @@ -1645,7 +1719,7 @@ type ListHeldRequest struct { func (x *ListHeldRequest) Reset() { *x = ListHeldRequest{} - mi := &file_host_installation_v1_installation_proto_msgTypes[24] + mi := &file_host_installation_v1_installation_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1657,7 +1731,7 @@ func (x *ListHeldRequest) String() string { func (*ListHeldRequest) ProtoMessage() {} func (x *ListHeldRequest) ProtoReflect() protoreflect.Message { - mi := &file_host_installation_v1_installation_proto_msgTypes[24] + mi := &file_host_installation_v1_installation_proto_msgTypes[25] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1670,7 +1744,7 @@ func (x *ListHeldRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListHeldRequest.ProtoReflect.Descriptor instead. func (*ListHeldRequest) Descriptor() ([]byte, []int) { - return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{24} + return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{25} } func (x *ListHeldRequest) GetOrganizationId() string { @@ -1692,7 +1766,7 @@ type ListHeldResponse struct { func (x *ListHeldResponse) Reset() { *x = ListHeldResponse{} - mi := &file_host_installation_v1_installation_proto_msgTypes[25] + mi := &file_host_installation_v1_installation_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1704,7 +1778,7 @@ func (x *ListHeldResponse) String() string { func (*ListHeldResponse) ProtoMessage() {} func (x *ListHeldResponse) ProtoReflect() protoreflect.Message { - mi := &file_host_installation_v1_installation_proto_msgTypes[25] + mi := &file_host_installation_v1_installation_proto_msgTypes[26] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1717,7 +1791,7 @@ func (x *ListHeldResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListHeldResponse.ProtoReflect.Descriptor instead. func (*ListHeldResponse) Descriptor() ([]byte, []int) { - return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{25} + return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{26} } func (x *ListHeldResponse) GetInstallations() []*HeldInstallation { @@ -1740,7 +1814,7 @@ type HeldInstallation struct { func (x *HeldInstallation) Reset() { *x = HeldInstallation{} - mi := &file_host_installation_v1_installation_proto_msgTypes[26] + mi := &file_host_installation_v1_installation_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1752,7 +1826,7 @@ func (x *HeldInstallation) String() string { func (*HeldInstallation) ProtoMessage() {} func (x *HeldInstallation) ProtoReflect() protoreflect.Message { - mi := &file_host_installation_v1_installation_proto_msgTypes[26] + mi := &file_host_installation_v1_installation_proto_msgTypes[27] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1765,7 +1839,7 @@ func (x *HeldInstallation) ProtoReflect() protoreflect.Message { // Deprecated: Use HeldInstallation.ProtoReflect.Descriptor instead. func (*HeldInstallation) Descriptor() ([]byte, []int) { - return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{26} + return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{27} } func (x *HeldInstallation) GetInstallation() *Installation { @@ -1865,14 +1939,19 @@ const file_host_installation_v1_installation_proto_rawDesc = "" + "\x06target\x18\x01 \x01(\tR\x06target\x12\x1e\n" + "\n" + "generation\x18\x02 \x01(\x04R\n" + - "generation\"\xce\x02\n" + + "generation\"\xcf\x03\n" + "\rReviewedOffer\x12A\n" + "\bapproval\x18\x01 \x01(\v2%.host.installation.v1.ConsentApprovalR\bapproval\x12\x16\n" + "\x06target\x18\x02 \x01(\tR\x06target\x125\n" + "\x05roles\x18\x03 \x03(\v2\x1f.host.installation.v1.OfferRoleR\x05roles\x12F\n" + "\finstallation\x18\x04 \x01(\v2\".host.installation.v1.InstallationR\finstallation\x12<\n" + "\aceiling\x18\x05 \x03(\v2\".host.installation.v1.OfferBindingR\aceiling\x12%\n" + - "\x0etools_composed\x18\x06 \x01(\bR\rtoolsComposed\"z\n" + + "\x0etools_composed\x18\x06 \x01(\bR\rtoolsComposed\x12+\n" + + "\x11installation_role\x18\a \x01(\tR\x10installationRole\x12R\n" + + "\x11edge_dependencies\x18\b \x03(\v2%.host.installation.v1.OfferDependencyR\x10edgeDependencies\"E\n" + + "\x0fOfferDependency\x12\x16\n" + + "\x06module\x18\x01 \x01(\tR\x06module\x12\x1a\n" + + "\bpresence\x18\x02 \x01(\tR\bpresence\"z\n" + "\fOfferBinding\x12\x18\n" + "\abinding\x18\x01 \x01(\tR\abinding\x12\x16\n" + "\x06module\x18\x02 \x01(\tR\x06module\x128\n" + @@ -1934,7 +2013,7 @@ func file_host_installation_v1_installation_proto_rawDescGZIP() []byte { } var file_host_installation_v1_installation_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_host_installation_v1_installation_proto_msgTypes = make([]protoimpl.MessageInfo, 27) +var file_host_installation_v1_installation_proto_msgTypes = make([]protoimpl.MessageInfo, 28) var file_host_installation_v1_installation_proto_goTypes = []any{ (Installation_Status)(0), // 0: host.installation.v1.Installation.Status (*CurrentInstallationRequest)(nil), // 1: host.installation.v1.CurrentInstallationRequest @@ -1956,14 +2035,15 @@ var file_host_installation_v1_installation_proto_goTypes = []any{ (*ListOffersResponse)(nil), // 17: host.installation.v1.ListOffersResponse (*Offer)(nil), // 18: host.installation.v1.Offer (*ReviewedOffer)(nil), // 19: host.installation.v1.ReviewedOffer - (*OfferBinding)(nil), // 20: host.installation.v1.OfferBinding - (*OfferRole)(nil), // 21: host.installation.v1.OfferRole - (*OfferScope)(nil), // 22: host.installation.v1.OfferScope - (*OfferCapability)(nil), // 23: host.installation.v1.OfferCapability - (*OfferTool)(nil), // 24: host.installation.v1.OfferTool - (*ListHeldRequest)(nil), // 25: host.installation.v1.ListHeldRequest - (*ListHeldResponse)(nil), // 26: host.installation.v1.ListHeldResponse - (*HeldInstallation)(nil), // 27: host.installation.v1.HeldInstallation + (*OfferDependency)(nil), // 20: host.installation.v1.OfferDependency + (*OfferBinding)(nil), // 21: host.installation.v1.OfferBinding + (*OfferRole)(nil), // 22: host.installation.v1.OfferRole + (*OfferScope)(nil), // 23: host.installation.v1.OfferScope + (*OfferCapability)(nil), // 24: host.installation.v1.OfferCapability + (*OfferTool)(nil), // 25: host.installation.v1.OfferTool + (*ListHeldRequest)(nil), // 26: host.installation.v1.ListHeldRequest + (*ListHeldResponse)(nil), // 27: host.installation.v1.ListHeldResponse + (*HeldInstallation)(nil), // 28: host.installation.v1.HeldInstallation } var file_host_installation_v1_installation_proto_depIdxs = []int32{ 5, // 0: host.installation.v1.InstallRequest.approval:type_name -> host.installation.v1.ConsentApproval @@ -1973,43 +2053,44 @@ var file_host_installation_v1_installation_proto_depIdxs = []int32{ 13, // 4: host.installation.v1.InstallationNotice.installation:type_name -> host.installation.v1.Installation 18, // 5: host.installation.v1.ListOffersResponse.offers:type_name -> host.installation.v1.Offer 5, // 6: host.installation.v1.ReviewedOffer.approval:type_name -> host.installation.v1.ConsentApproval - 21, // 7: host.installation.v1.ReviewedOffer.roles:type_name -> host.installation.v1.OfferRole + 22, // 7: host.installation.v1.ReviewedOffer.roles:type_name -> host.installation.v1.OfferRole 13, // 8: host.installation.v1.ReviewedOffer.installation:type_name -> host.installation.v1.Installation - 20, // 9: host.installation.v1.ReviewedOffer.ceiling:type_name -> host.installation.v1.OfferBinding - 22, // 10: host.installation.v1.OfferBinding.scopes:type_name -> host.installation.v1.OfferScope - 22, // 11: host.installation.v1.OfferRole.scopes:type_name -> host.installation.v1.OfferScope - 23, // 12: host.installation.v1.OfferRole.capabilities:type_name -> host.installation.v1.OfferCapability - 22, // 13: host.installation.v1.OfferCapability.requires:type_name -> host.installation.v1.OfferScope - 24, // 14: host.installation.v1.OfferCapability.tools:type_name -> host.installation.v1.OfferTool - 27, // 15: host.installation.v1.ListHeldResponse.installations:type_name -> host.installation.v1.HeldInstallation - 13, // 16: host.installation.v1.HeldInstallation.installation:type_name -> host.installation.v1.Installation - 1, // 17: host.installation.v1.InstallationService.Current:input_type -> host.installation.v1.CurrentInstallationRequest - 3, // 18: host.installation.v1.InstallationService.Install:input_type -> host.installation.v1.InstallRequest - 4, // 19: host.installation.v1.InstallationService.Review:input_type -> host.installation.v1.ReviewRequest - 6, // 20: host.installation.v1.InstallationService.GrantRole:input_type -> host.installation.v1.GrantRoleRequest - 7, // 21: host.installation.v1.InstallationService.RevokeRole:input_type -> host.installation.v1.RevokeRoleRequest - 8, // 22: host.installation.v1.InstallationService.ListRoleGrants:input_type -> host.installation.v1.ListRoleGrantsRequest - 11, // 23: host.installation.v1.InstallationService.List:input_type -> host.installation.v1.ListRequest - 25, // 24: host.installation.v1.InstallationService.ListHeld:input_type -> host.installation.v1.ListHeldRequest - 14, // 25: host.installation.v1.InstallableService.Install:input_type -> host.installation.v1.InstallationNotice - 16, // 26: host.installation.v1.OfferService.ListOffers:input_type -> host.installation.v1.ListOffersRequest - 4, // 27: host.installation.v1.OfferService.ReviewOffer:input_type -> host.installation.v1.ReviewRequest - 2, // 28: host.installation.v1.InstallationService.Current:output_type -> host.installation.v1.CurrentInstallation - 13, // 29: host.installation.v1.InstallationService.Install:output_type -> host.installation.v1.Installation - 5, // 30: host.installation.v1.InstallationService.Review:output_type -> host.installation.v1.ConsentApproval - 13, // 31: host.installation.v1.InstallationService.GrantRole:output_type -> host.installation.v1.Installation - 13, // 32: host.installation.v1.InstallationService.RevokeRole:output_type -> host.installation.v1.Installation - 9, // 33: host.installation.v1.InstallationService.ListRoleGrants:output_type -> host.installation.v1.ListRoleGrantsResponse - 12, // 34: host.installation.v1.InstallationService.List:output_type -> host.installation.v1.ListResponse - 26, // 35: host.installation.v1.InstallationService.ListHeld:output_type -> host.installation.v1.ListHeldResponse - 15, // 36: host.installation.v1.InstallableService.Install:output_type -> host.installation.v1.InstallationAck - 17, // 37: host.installation.v1.OfferService.ListOffers:output_type -> host.installation.v1.ListOffersResponse - 19, // 38: host.installation.v1.OfferService.ReviewOffer:output_type -> host.installation.v1.ReviewedOffer - 28, // [28:39] is the sub-list for method output_type - 17, // [17:28] is the sub-list for method input_type - 17, // [17:17] is the sub-list for extension type_name - 17, // [17:17] is the sub-list for extension extendee - 0, // [0:17] is the sub-list for field type_name + 21, // 9: host.installation.v1.ReviewedOffer.ceiling:type_name -> host.installation.v1.OfferBinding + 20, // 10: host.installation.v1.ReviewedOffer.edge_dependencies:type_name -> host.installation.v1.OfferDependency + 23, // 11: host.installation.v1.OfferBinding.scopes:type_name -> host.installation.v1.OfferScope + 23, // 12: host.installation.v1.OfferRole.scopes:type_name -> host.installation.v1.OfferScope + 24, // 13: host.installation.v1.OfferRole.capabilities:type_name -> host.installation.v1.OfferCapability + 23, // 14: host.installation.v1.OfferCapability.requires:type_name -> host.installation.v1.OfferScope + 25, // 15: host.installation.v1.OfferCapability.tools:type_name -> host.installation.v1.OfferTool + 28, // 16: host.installation.v1.ListHeldResponse.installations:type_name -> host.installation.v1.HeldInstallation + 13, // 17: host.installation.v1.HeldInstallation.installation:type_name -> host.installation.v1.Installation + 1, // 18: host.installation.v1.InstallationService.Current:input_type -> host.installation.v1.CurrentInstallationRequest + 3, // 19: host.installation.v1.InstallationService.Install:input_type -> host.installation.v1.InstallRequest + 4, // 20: host.installation.v1.InstallationService.Review:input_type -> host.installation.v1.ReviewRequest + 6, // 21: host.installation.v1.InstallationService.GrantRole:input_type -> host.installation.v1.GrantRoleRequest + 7, // 22: host.installation.v1.InstallationService.RevokeRole:input_type -> host.installation.v1.RevokeRoleRequest + 8, // 23: host.installation.v1.InstallationService.ListRoleGrants:input_type -> host.installation.v1.ListRoleGrantsRequest + 11, // 24: host.installation.v1.InstallationService.List:input_type -> host.installation.v1.ListRequest + 26, // 25: host.installation.v1.InstallationService.ListHeld:input_type -> host.installation.v1.ListHeldRequest + 14, // 26: host.installation.v1.InstallableService.Install:input_type -> host.installation.v1.InstallationNotice + 16, // 27: host.installation.v1.OfferService.ListOffers:input_type -> host.installation.v1.ListOffersRequest + 4, // 28: host.installation.v1.OfferService.ReviewOffer:input_type -> host.installation.v1.ReviewRequest + 2, // 29: host.installation.v1.InstallationService.Current:output_type -> host.installation.v1.CurrentInstallation + 13, // 30: host.installation.v1.InstallationService.Install:output_type -> host.installation.v1.Installation + 5, // 31: host.installation.v1.InstallationService.Review:output_type -> host.installation.v1.ConsentApproval + 13, // 32: host.installation.v1.InstallationService.GrantRole:output_type -> host.installation.v1.Installation + 13, // 33: host.installation.v1.InstallationService.RevokeRole:output_type -> host.installation.v1.Installation + 9, // 34: host.installation.v1.InstallationService.ListRoleGrants:output_type -> host.installation.v1.ListRoleGrantsResponse + 12, // 35: host.installation.v1.InstallationService.List:output_type -> host.installation.v1.ListResponse + 27, // 36: host.installation.v1.InstallationService.ListHeld:output_type -> host.installation.v1.ListHeldResponse + 15, // 37: host.installation.v1.InstallableService.Install:output_type -> host.installation.v1.InstallationAck + 17, // 38: host.installation.v1.OfferService.ListOffers:output_type -> host.installation.v1.ListOffersResponse + 19, // 39: host.installation.v1.OfferService.ReviewOffer:output_type -> host.installation.v1.ReviewedOffer + 29, // [29:40] is the sub-list for method output_type + 18, // [18:29] is the sub-list for method input_type + 18, // [18:18] is the sub-list for extension type_name + 18, // [18:18] is the sub-list for extension extendee + 0, // [0:18] is the sub-list for field type_name } func init() { file_host_installation_v1_installation_proto_init() } @@ -2023,7 +2104,7 @@ func file_host_installation_v1_installation_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_host_installation_v1_installation_proto_rawDesc), len(file_host_installation_v1_installation_proto_rawDesc)), NumEnums: 1, - NumMessages: 27, + NumMessages: 28, NumExtensions: 0, NumServices: 3, }, diff --git a/libraries/authority-client/library.codefly.yaml b/libraries/authority-client/library.codefly.yaml index 5022f517..0dfd3dcb 100644 --- a/libraries/authority-client/library.codefly.yaml +++ b/libraries/authority-client/library.codefly.yaml @@ -15,15 +15,22 @@ languages: exports: - '@codefly-dev/authority-client' sources: - - package: local + - package: codefly/authority version: 0.1.0 module: authority service: local endpoint: grpc - contract-digest: sha256:2d74a1ad05ecab544606e2e52630cc5cb6c16729371f091262dfb45a686d2712 + contract-digest: sha256:b49a5399e647c61b1991fc22bf7fcf8262ab17333dbe01b4afd1d16c346f3ea7 services: - LocalService + - ArtifactService + - AuthorityService + - CompositionService + - InstallationService + - OfferService + - SessionService + as: api generated-by: - codefly: 0.1.219 + codefly: 0.1.221 companion: ghcr.io/codefly-dev/proto:0.0.18 facade: false diff --git a/libraries/authority-client/python/authority_client/_gen/host/composition/v1/composition_pb2.py b/libraries/authority-client/python/authority_client/_gen/host/composition/v1/composition_pb2.py index 75733b91..7cf1d4c5 100644 --- a/libraries/authority-client/python/authority_client/_gen/host/composition/v1/composition_pb2.py +++ b/libraries/authority-client/python/authority_client/_gen/host/composition/v1/composition_pb2.py @@ -25,7 +25,7 @@ from authority_client._gen.host.authority.v1 import work_context_pb2 as host_dot_authority_dot_v1_dot_work__context__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n%host/composition/v1/composition.proto\x12\x13host.composition.v1\x1a$host/authority/v1/work_context.proto\"\xff\x01\n\x14\x41pplyManifestRequest\x12\x41\n\x08manifest\x18\x01 \x01(\x0b\x32%.host.composition.v1.ModuleManifestV1R\x08manifest\x12?\n\x07\x63\x61rrier\x18\x02 \x01(\x0b\x32%.host.composition.v1.SignedManifestV1R\x07\x63\x61rrier\x12\x1e\n\ncredential\x18\x03 \x01(\tR\ncredential\x12\x43\n\tauthority\x18\x04 \x01(\x0b\x32%.host.composition.v1.SignedManifestV1R\tauthority\"\xd9\x02\n\x13\x41uthorityManifestV1\x12\x16\n\x06module\x18\x01 \x01(\tR\x06module\x12\x1e\n\ngeneration\x18\x02 \x01(\x04R\ngeneration\x12\x30\n\x14\x63ore_manifest_digest\x18\x03 \x01(\tR\x12\x63oreManifestDigest\x12=\n\x07methods\x18\x04 \x03(\x0b\x32#.host.composition.v1.MethodPolicyV1R\x07methods\x12\x45\n\x0escope_ceilings\x18\x05 \x03(\x0b\x32\x1e.host.authority.v1.WorkScopeV1R\rscopeCeilings\x12\x31\n\x05roles\x18\x06 \x03(\x0b\x32\x1b.host.composition.v1.RoleV1R\x05roles\x12\x1f\n\x0bowner_aware\x18\x07 \x01(\x08R\nownerAware\"\x92\x01\n\x0eMethodPolicyV1\x12\x16\n\x06method\x18\x01 \x01(\tR\x06method\x12:\n\x08requires\x18\x02 \x03(\x0b\x32\x1e.host.authority.v1.WorkScopeV1R\x08requires\x12,\n\x12\x61ny_checked_caller\x18\x03 \x01(\x08R\x10\x61nyCheckedCaller\"J\n\x10SignedManifestV1\x12\x1a\n\x08\x64ocument\x18\x01 \x01(\x0cR\x08\x64ocument\x12\x1a\n\x08\x65nvelope\x18\x02 \x01(\x0cR\x08\x65nvelope\"\x80\x01\n\x15\x41pplyManifestResponse\x12\x1c\n\tprincipal\x18\x01 \x01(\tR\tprincipal\x12\x1e\n\ngeneration\x18\x02 \x01(\x04R\ngeneration\x12)\n\x10installation_ids\x18\x03 \x03(\tR\x0finstallationIds\"\xe8\x02\n\x10ModuleManifestV1\x12\x16\n\x06module\x18\x01 \x01(\tR\x06module\x12\x1c\n\tprincipal\x18\x02 \x01(\tR\tprincipal\x12\x12\n\x04kind\x18\x03 \x01(\tR\x04kind\x12\x1e\n\ngeneration\x18\x04 \x01(\x04R\ngeneration\x12:\n\x08\x62indings\x18\x05 \x03(\x0b\x32\x1e.host.composition.v1.BindingV1R\x08\x62indings\x12J\n\x0eresource_kinds\x18\x06 \x03(\x0b\x32#.host.composition.v1.ResourceKindV1R\rresourceKinds\x12/\n\x04page\x18\x07 \x01(\x0b\x32\x1b.host.composition.v1.PageV1R\x04page\x12\x31\n\x05roles\x18\x08 \x03(\x0b\x32\x1b.host.composition.v1.RoleV1R\x05roles\"\xb0\x01\n\x06RoleV1\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x36\n\x06scopes\x18\x02 \x03(\x0b\x32\x1e.host.authority.v1.WorkScopeV1R\x06scopes\x12Z\n\x19installation_owned_scopes\x18\x03 \x03(\x0b\x32\x1e.host.authority.v1.WorkScopeV1R\x17installationOwnedScopes\"\x8a\x02\n\tBindingV1\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x1a\n\x08\x61udience\x18\x02 \x01(\tR\x08\x61udience\x12\x43\n\rinvoke_scopes\x18\x03 \x03(\x0b\x32\x1e.host.authority.v1.WorkScopeV1R\x0cinvokeScopes\x12G\n\x0fheadless_scopes\x18\x04 \x03(\x0b\x32\x1e.host.authority.v1.WorkScopeV1R\x0eheadlessScopes\x12\x43\n\rlookup_scopes\x18\x05 \x03(\x0b\x32\x1e.host.authority.v1.WorkScopeV1R\x0clookupScopes\">\n\x0eResourceKindV1\x12\x12\n\x04kind\x18\x01 \x01(\tR\x04kind\x12\x18\n\x07\x61\x63tions\x18\x02 \x03(\tR\x07\x61\x63tions\"4\n\x06PageV1\x12\x14\n\x05title\x18\x01 \x01(\tR\x05title\x12\x14\n\x05order\x18\x02 \x01(\x05R\x05order2|\n\x12\x43ompositionService\x12\x66\n\rApplyManifest\x12).host.composition.v1.ApplyManifestRequest\x1a*.host.composition.v1.ApplyManifestResponseB3Z1gateway/pkg/gen/host/composition/v1;compositionv1b\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n%host/composition/v1/composition.proto\x12\x13host.composition.v1\x1a$host/authority/v1/work_context.proto\"\xff\x01\n\x14\x41pplyManifestRequest\x12\x41\n\x08manifest\x18\x01 \x01(\x0b\x32%.host.composition.v1.ModuleManifestV1R\x08manifest\x12?\n\x07\x63\x61rrier\x18\x02 \x01(\x0b\x32%.host.composition.v1.SignedManifestV1R\x07\x63\x61rrier\x12\x1e\n\ncredential\x18\x03 \x01(\tR\ncredential\x12\x43\n\tauthority\x18\x04 \x01(\x0b\x32%.host.composition.v1.SignedManifestV1R\tauthority\"\xda\x03\n\x13\x41uthorityManifestV1\x12\x16\n\x06module\x18\x01 \x01(\tR\x06module\x12\x1e\n\ngeneration\x18\x02 \x01(\x04R\ngeneration\x12\x30\n\x14\x63ore_manifest_digest\x18\x03 \x01(\tR\x12\x63oreManifestDigest\x12=\n\x07methods\x18\x04 \x03(\x0b\x32#.host.composition.v1.MethodPolicyV1R\x07methods\x12\x45\n\x0escope_ceilings\x18\x05 \x03(\x0b\x32\x1e.host.authority.v1.WorkScopeV1R\rscopeCeilings\x12\x31\n\x05roles\x18\x06 \x03(\x0b\x32\x1b.host.composition.v1.RoleV1R\x05roles\x12\x1f\n\x0bowner_aware\x18\x07 \x01(\x08R\nownerAware\x12+\n\x11installation_role\x18\x08 \x01(\tR\x10installationRole\x12R\n\x11\x65\x64ge_dependencies\x18\t \x03(\x0b\x32%.host.composition.v1.EdgeDependencyV1R\x10\x65\x64geDependencies\"F\n\x10\x45\x64geDependencyV1\x12\x16\n\x06module\x18\x01 \x01(\tR\x06module\x12\x1a\n\x08presence\x18\x02 \x01(\tR\x08presence\"\x92\x01\n\x0eMethodPolicyV1\x12\x16\n\x06method\x18\x01 \x01(\tR\x06method\x12:\n\x08requires\x18\x02 \x03(\x0b\x32\x1e.host.authority.v1.WorkScopeV1R\x08requires\x12,\n\x12\x61ny_checked_caller\x18\x03 \x01(\x08R\x10\x61nyCheckedCaller\"J\n\x10SignedManifestV1\x12\x1a\n\x08\x64ocument\x18\x01 \x01(\x0cR\x08\x64ocument\x12\x1a\n\x08\x65nvelope\x18\x02 \x01(\x0cR\x08\x65nvelope\"\x80\x01\n\x15\x41pplyManifestResponse\x12\x1c\n\tprincipal\x18\x01 \x01(\tR\tprincipal\x12\x1e\n\ngeneration\x18\x02 \x01(\x04R\ngeneration\x12)\n\x10installation_ids\x18\x03 \x03(\tR\x0finstallationIds\"\xe8\x02\n\x10ModuleManifestV1\x12\x16\n\x06module\x18\x01 \x01(\tR\x06module\x12\x1c\n\tprincipal\x18\x02 \x01(\tR\tprincipal\x12\x12\n\x04kind\x18\x03 \x01(\tR\x04kind\x12\x1e\n\ngeneration\x18\x04 \x01(\x04R\ngeneration\x12:\n\x08\x62indings\x18\x05 \x03(\x0b\x32\x1e.host.composition.v1.BindingV1R\x08\x62indings\x12J\n\x0eresource_kinds\x18\x06 \x03(\x0b\x32#.host.composition.v1.ResourceKindV1R\rresourceKinds\x12/\n\x04page\x18\x07 \x01(\x0b\x32\x1b.host.composition.v1.PageV1R\x04page\x12\x31\n\x05roles\x18\x08 \x03(\x0b\x32\x1b.host.composition.v1.RoleV1R\x05roles\"\xb0\x01\n\x06RoleV1\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x36\n\x06scopes\x18\x02 \x03(\x0b\x32\x1e.host.authority.v1.WorkScopeV1R\x06scopes\x12Z\n\x19installation_owned_scopes\x18\x03 \x03(\x0b\x32\x1e.host.authority.v1.WorkScopeV1R\x17installationOwnedScopes\"\x8a\x02\n\tBindingV1\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x1a\n\x08\x61udience\x18\x02 \x01(\tR\x08\x61udience\x12\x43\n\rinvoke_scopes\x18\x03 \x03(\x0b\x32\x1e.host.authority.v1.WorkScopeV1R\x0cinvokeScopes\x12G\n\x0fheadless_scopes\x18\x04 \x03(\x0b\x32\x1e.host.authority.v1.WorkScopeV1R\x0eheadlessScopes\x12\x43\n\rlookup_scopes\x18\x05 \x03(\x0b\x32\x1e.host.authority.v1.WorkScopeV1R\x0clookupScopes\">\n\x0eResourceKindV1\x12\x12\n\x04kind\x18\x01 \x01(\tR\x04kind\x12\x18\n\x07\x61\x63tions\x18\x02 \x03(\tR\x07\x61\x63tions\"4\n\x06PageV1\x12\x14\n\x05title\x18\x01 \x01(\tR\x05title\x12\x14\n\x05order\x18\x02 \x01(\x05R\x05order2|\n\x12\x43ompositionService\x12\x66\n\rApplyManifest\x12).host.composition.v1.ApplyManifestRequest\x1a*.host.composition.v1.ApplyManifestResponseB3Z1gateway/pkg/gen/host/composition/v1;compositionv1b\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -36,23 +36,25 @@ _globals['_APPLYMANIFESTREQUEST']._serialized_start=101 _globals['_APPLYMANIFESTREQUEST']._serialized_end=356 _globals['_AUTHORITYMANIFESTV1']._serialized_start=359 - _globals['_AUTHORITYMANIFESTV1']._serialized_end=704 - _globals['_METHODPOLICYV1']._serialized_start=707 - _globals['_METHODPOLICYV1']._serialized_end=853 - _globals['_SIGNEDMANIFESTV1']._serialized_start=855 - _globals['_SIGNEDMANIFESTV1']._serialized_end=929 - _globals['_APPLYMANIFESTRESPONSE']._serialized_start=932 - _globals['_APPLYMANIFESTRESPONSE']._serialized_end=1060 - _globals['_MODULEMANIFESTV1']._serialized_start=1063 - _globals['_MODULEMANIFESTV1']._serialized_end=1423 - _globals['_ROLEV1']._serialized_start=1426 - _globals['_ROLEV1']._serialized_end=1602 - _globals['_BINDINGV1']._serialized_start=1605 - _globals['_BINDINGV1']._serialized_end=1871 - _globals['_RESOURCEKINDV1']._serialized_start=1873 - _globals['_RESOURCEKINDV1']._serialized_end=1935 - _globals['_PAGEV1']._serialized_start=1937 - _globals['_PAGEV1']._serialized_end=1989 - _globals['_COMPOSITIONSERVICE']._serialized_start=1991 - _globals['_COMPOSITIONSERVICE']._serialized_end=2115 + _globals['_AUTHORITYMANIFESTV1']._serialized_end=833 + _globals['_EDGEDEPENDENCYV1']._serialized_start=835 + _globals['_EDGEDEPENDENCYV1']._serialized_end=905 + _globals['_METHODPOLICYV1']._serialized_start=908 + _globals['_METHODPOLICYV1']._serialized_end=1054 + _globals['_SIGNEDMANIFESTV1']._serialized_start=1056 + _globals['_SIGNEDMANIFESTV1']._serialized_end=1130 + _globals['_APPLYMANIFESTRESPONSE']._serialized_start=1133 + _globals['_APPLYMANIFESTRESPONSE']._serialized_end=1261 + _globals['_MODULEMANIFESTV1']._serialized_start=1264 + _globals['_MODULEMANIFESTV1']._serialized_end=1624 + _globals['_ROLEV1']._serialized_start=1627 + _globals['_ROLEV1']._serialized_end=1803 + _globals['_BINDINGV1']._serialized_start=1806 + _globals['_BINDINGV1']._serialized_end=2072 + _globals['_RESOURCEKINDV1']._serialized_start=2074 + _globals['_RESOURCEKINDV1']._serialized_end=2136 + _globals['_PAGEV1']._serialized_start=2138 + _globals['_PAGEV1']._serialized_end=2190 + _globals['_COMPOSITIONSERVICE']._serialized_start=2192 + _globals['_COMPOSITIONSERVICE']._serialized_end=2316 # @@protoc_insertion_point(module_scope) diff --git a/libraries/authority-client/python/authority_client/_gen/host/installation/v1/installation_pb2.py b/libraries/authority-client/python/authority_client/_gen/host/installation/v1/installation_pb2.py index 7e19d38c..ee775561 100644 --- a/libraries/authority-client/python/authority_client/_gen/host/installation/v1/installation_pb2.py +++ b/libraries/authority-client/python/authority_client/_gen/host/installation/v1/installation_pb2.py @@ -24,7 +24,7 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\'host/installation/v1/installation.proto\x12\x14host.installation.v1\"j\n\x1a\x43urrentInstallationRequest\x12!\n\x0cparent_token\x18\x01 \x01(\tR\x0bparentToken\x12)\n\x10\x61\x63tor_credential\x18\x02 \x01(\tR\x0f\x61\x63torCredential\"\xa0\x01\n\x13\x43urrentInstallation\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\'\n\x0forganization_id\x18\x02 \x01(\tR\x0eorganizationId\x12\x16\n\x06target\x18\x03 \x01(\tR\x06target\x12\x1a\n\x08revision\x18\x04 \x01(\x04R\x08revision\x12\x1c\n\tnamespace\x18\x05 \x01(\tR\tnamespace\"\xd0\x01\n\x0eInstallRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x16\n\x06target\x18\x02 \x01(\tR\x06target\x12\x12\n\x04role\x18\x03 \x01(\tR\x04role\x12&\n\x0fowner_person_id\x18\x04 \x01(\tR\rownerPersonId\x12\x41\n\x08\x61pproval\x18\x05 \x01(\x0b\x32%.host.installation.v1.ConsentApprovalR\x08\x61pproval\"P\n\rReviewRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x16\n\x06target\x18\x02 \x01(\tR\x06target\"\xb6\x01\n\x0f\x43onsentApproval\x12\x1b\n\ttarget_id\x18\x01 \x01(\tR\x08targetId\x12\x1e\n\ngeneration\x18\x02 \x01(\x04R\ngeneration\x12+\n\x11\x65xpected_revision\x18\x03 \x01(\x04R\x10\x65xpectedRevision\x12!\n\x0coperation_id\x18\x04 \x01(\tR\x0boperationId\x12\x16\n\x06\x64igest\x18\x05 \x01(\tR\x06\x64igest\"\xae\x01\n\x10GrantRoleRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\'\n\x0finstallation_id\x18\x02 \x01(\tR\x0einstallationId\x12\x12\n\x04role\x18\x03 \x01(\tR\x04role\x12\x1b\n\tperson_id\x18\x04 \x01(\tR\x08personId\x12\x17\n\x07team_id\x18\x05 \x01(\tR\x06teamId\"\xaf\x01\n\x11RevokeRoleRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\'\n\x0finstallation_id\x18\x02 \x01(\tR\x0einstallationId\x12\x12\n\x04role\x18\x03 \x01(\tR\x04role\x12\x1b\n\tperson_id\x18\x04 \x01(\tR\x08personId\x12\x17\n\x07team_id\x18\x05 \x01(\tR\x06teamId\"i\n\x15ListRoleGrantsRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\'\n\x0finstallation_id\x18\x02 \x01(\tR\x0einstallationId\"Q\n\x16ListRoleGrantsResponse\x12\x37\n\x06grants\x18\x01 \x03(\x0b\x32\x1f.host.installation.v1.RoleGrantR\x06grants\"U\n\tRoleGrant\x12\x12\n\x04role\x18\x01 \x01(\tR\x04role\x12\x1b\n\tperson_id\x18\x02 \x01(\tR\x08personId\x12\x17\n\x07team_id\x18\x03 \x01(\tR\x06teamId\"6\n\x0bListRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\"X\n\x0cListResponse\x12H\n\rinstallations\x18\x01 \x03(\x0b\x32\".host.installation.v1.InstallationR\rinstallations\"\xb0\x03\n\x0cInstallation\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\'\n\x0forganization_id\x18\x02 \x01(\tR\x0eorganizationId\x12\x16\n\x06target\x18\x03 \x01(\tR\x06target\x12\x41\n\x06status\x18\x04 \x01(\x0e\x32).host.installation.v1.Installation.StatusR\x06status\x12\x1a\n\x08revision\x18\x05 \x01(\x04R\x08revision\x12\x12\n\x04role\x18\x06 \x01(\tR\x04role\x12\x1c\n\tnamespace\x18\t \x01(\tR\tnamespace\x12!\n\x0cprincipal_id\x18\n \x01(\tR\x0bprincipalId\x12\x14\n\x05roles\x18\x0b \x03(\tR\x05roles\"[\n\x06Status\x12\x16\n\x12STATUS_UNSPECIFIED\x10\x00\x12\x12\n\x0eSTATUS_PENDING\x10\x01\x12\x11\n\rSTATUS_ACTIVE\x10\x02\x12\x12\n\x0eSTATUS_REVOKED\x10\x03J\x04\x08\x07\x10\x08J\x04\x08\x08\x10\tR\x08team_idsR\x12\x61gent_principal_id\"\\\n\x12InstallationNotice\x12\x46\n\x0cinstallation\x18\x01 \x01(\x0b\x32\".host.installation.v1.InstallationR\x0cinstallation\"\x11\n\x0fInstallationAck\"<\n\x11ListOffersRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\"I\n\x12ListOffersResponse\x12\x33\n\x06offers\x18\x01 \x03(\x0b\x32\x1b.host.installation.v1.OfferR\x06offers\"?\n\x05Offer\x12\x16\n\x06target\x18\x01 \x01(\tR\x06target\x12\x1e\n\ngeneration\x18\x02 \x01(\x04R\ngeneration\"\xce\x02\n\rReviewedOffer\x12\x41\n\x08\x61pproval\x18\x01 \x01(\x0b\x32%.host.installation.v1.ConsentApprovalR\x08\x61pproval\x12\x16\n\x06target\x18\x02 \x01(\tR\x06target\x12\x35\n\x05roles\x18\x03 \x03(\x0b\x32\x1f.host.installation.v1.OfferRoleR\x05roles\x12\x46\n\x0cinstallation\x18\x04 \x01(\x0b\x32\".host.installation.v1.InstallationR\x0cinstallation\x12<\n\x07\x63\x65iling\x18\x05 \x03(\x0b\x32\".host.installation.v1.OfferBindingR\x07\x63\x65iling\x12%\n\x0etools_composed\x18\x06 \x01(\x08R\rtoolsComposed\"z\n\x0cOfferBinding\x12\x18\n\x07\x62inding\x18\x01 \x01(\tR\x07\x62inding\x12\x16\n\x06module\x18\x02 \x01(\tR\x06module\x12\x38\n\x06scopes\x18\x03 \x03(\x0b\x32 .host.installation.v1.OfferScopeR\x06scopes\"\xa4\x01\n\tOfferRole\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x38\n\x06scopes\x18\x02 \x03(\x0b\x32 .host.installation.v1.OfferScopeR\x06scopes\x12I\n\x0c\x63\x61pabilities\x18\x03 \x03(\x0b\x32%.host.installation.v1.OfferCapabilityR\x0c\x63\x61pabilities\"\x93\x01\n\nOfferScope\x12#\n\rresource_kind\x18\x01 \x01(\tR\x0cresourceKind\x12\x18\n\x07\x61\x63tions\x18\x02 \x03(\tR\x07\x61\x63tions\x12#\n\rattached_only\x18\x03 \x01(\x08R\x0c\x61ttachedOnly\x12!\n\x0cresource_ids\x18\x04 \x03(\tR\x0bresourceIds\"\xbc\x01\n\x0fOfferCapability\x12\x16\n\x06module\x18\x01 \x01(\tR\x06module\x12\x1c\n\toperation\x18\x02 \x01(\tR\toperation\x12<\n\x08requires\x18\x03 \x03(\x0b\x32 .host.installation.v1.OfferScopeR\x08requires\x12\x35\n\x05tools\x18\x04 \x03(\x0b\x32\x1f.host.installation.v1.OfferToolR\x05tools\"O\n\tOfferTool\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n\x07\x62inding\x18\x02 \x01(\tR\x07\x62inding\x12\x14\n\x05write\x18\x03 \x01(\x08R\x05write\":\n\x0fListHeldRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\"`\n\x10ListHeldResponse\x12L\n\rinstallations\x18\x01 \x03(\x0b\x32&.host.installation.v1.HeldInstallationR\rinstallations\"p\n\x10HeldInstallation\x12\x46\n\x0cinstallation\x18\x01 \x01(\x0b\x32\".host.installation.v1.InstallationR\x0cinstallation\x12\x14\n\x05roles\x18\x02 \x03(\tR\x05roles2\xf3\x05\n\x13InstallationService\x12\x66\n\x07\x43urrent\x12\x30.host.installation.v1.CurrentInstallationRequest\x1a).host.installation.v1.CurrentInstallation\x12S\n\x07Install\x12$.host.installation.v1.InstallRequest\x1a\".host.installation.v1.Installation\x12T\n\x06Review\x12#.host.installation.v1.ReviewRequest\x1a%.host.installation.v1.ConsentApproval\x12W\n\tGrantRole\x12&.host.installation.v1.GrantRoleRequest\x1a\".host.installation.v1.Installation\x12Y\n\nRevokeRole\x12\'.host.installation.v1.RevokeRoleRequest\x1a\".host.installation.v1.Installation\x12k\n\x0eListRoleGrants\x12+.host.installation.v1.ListRoleGrantsRequest\x1a,.host.installation.v1.ListRoleGrantsResponse\x12M\n\x04List\x12!.host.installation.v1.ListRequest\x1a\".host.installation.v1.ListResponse\x12Y\n\x08ListHeld\x12%.host.installation.v1.ListHeldRequest\x1a&.host.installation.v1.ListHeldResponse2p\n\x12InstallableService\x12Z\n\x07Install\x12(.host.installation.v1.InstallationNotice\x1a%.host.installation.v1.InstallationAck2\xc8\x01\n\x0cOfferService\x12_\n\nListOffers\x12\'.host.installation.v1.ListOffersRequest\x1a(.host.installation.v1.ListOffersResponse\x12W\n\x0bReviewOffer\x12#.host.installation.v1.ReviewRequest\x1a#.host.installation.v1.ReviewedOfferB5Z3gateway/pkg/gen/host/installation/v1;installationv1b\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\'host/installation/v1/installation.proto\x12\x14host.installation.v1\"j\n\x1a\x43urrentInstallationRequest\x12!\n\x0cparent_token\x18\x01 \x01(\tR\x0bparentToken\x12)\n\x10\x61\x63tor_credential\x18\x02 \x01(\tR\x0f\x61\x63torCredential\"\xa0\x01\n\x13\x43urrentInstallation\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\'\n\x0forganization_id\x18\x02 \x01(\tR\x0eorganizationId\x12\x16\n\x06target\x18\x03 \x01(\tR\x06target\x12\x1a\n\x08revision\x18\x04 \x01(\x04R\x08revision\x12\x1c\n\tnamespace\x18\x05 \x01(\tR\tnamespace\"\xd0\x01\n\x0eInstallRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x16\n\x06target\x18\x02 \x01(\tR\x06target\x12\x12\n\x04role\x18\x03 \x01(\tR\x04role\x12&\n\x0fowner_person_id\x18\x04 \x01(\tR\rownerPersonId\x12\x41\n\x08\x61pproval\x18\x05 \x01(\x0b\x32%.host.installation.v1.ConsentApprovalR\x08\x61pproval\"P\n\rReviewRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x16\n\x06target\x18\x02 \x01(\tR\x06target\"\xb6\x01\n\x0f\x43onsentApproval\x12\x1b\n\ttarget_id\x18\x01 \x01(\tR\x08targetId\x12\x1e\n\ngeneration\x18\x02 \x01(\x04R\ngeneration\x12+\n\x11\x65xpected_revision\x18\x03 \x01(\x04R\x10\x65xpectedRevision\x12!\n\x0coperation_id\x18\x04 \x01(\tR\x0boperationId\x12\x16\n\x06\x64igest\x18\x05 \x01(\tR\x06\x64igest\"\xae\x01\n\x10GrantRoleRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\'\n\x0finstallation_id\x18\x02 \x01(\tR\x0einstallationId\x12\x12\n\x04role\x18\x03 \x01(\tR\x04role\x12\x1b\n\tperson_id\x18\x04 \x01(\tR\x08personId\x12\x17\n\x07team_id\x18\x05 \x01(\tR\x06teamId\"\xaf\x01\n\x11RevokeRoleRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\'\n\x0finstallation_id\x18\x02 \x01(\tR\x0einstallationId\x12\x12\n\x04role\x18\x03 \x01(\tR\x04role\x12\x1b\n\tperson_id\x18\x04 \x01(\tR\x08personId\x12\x17\n\x07team_id\x18\x05 \x01(\tR\x06teamId\"i\n\x15ListRoleGrantsRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\'\n\x0finstallation_id\x18\x02 \x01(\tR\x0einstallationId\"Q\n\x16ListRoleGrantsResponse\x12\x37\n\x06grants\x18\x01 \x03(\x0b\x32\x1f.host.installation.v1.RoleGrantR\x06grants\"U\n\tRoleGrant\x12\x12\n\x04role\x18\x01 \x01(\tR\x04role\x12\x1b\n\tperson_id\x18\x02 \x01(\tR\x08personId\x12\x17\n\x07team_id\x18\x03 \x01(\tR\x06teamId\"6\n\x0bListRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\"X\n\x0cListResponse\x12H\n\rinstallations\x18\x01 \x03(\x0b\x32\".host.installation.v1.InstallationR\rinstallations\"\xb0\x03\n\x0cInstallation\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\'\n\x0forganization_id\x18\x02 \x01(\tR\x0eorganizationId\x12\x16\n\x06target\x18\x03 \x01(\tR\x06target\x12\x41\n\x06status\x18\x04 \x01(\x0e\x32).host.installation.v1.Installation.StatusR\x06status\x12\x1a\n\x08revision\x18\x05 \x01(\x04R\x08revision\x12\x12\n\x04role\x18\x06 \x01(\tR\x04role\x12\x1c\n\tnamespace\x18\t \x01(\tR\tnamespace\x12!\n\x0cprincipal_id\x18\n \x01(\tR\x0bprincipalId\x12\x14\n\x05roles\x18\x0b \x03(\tR\x05roles\"[\n\x06Status\x12\x16\n\x12STATUS_UNSPECIFIED\x10\x00\x12\x12\n\x0eSTATUS_PENDING\x10\x01\x12\x11\n\rSTATUS_ACTIVE\x10\x02\x12\x12\n\x0eSTATUS_REVOKED\x10\x03J\x04\x08\x07\x10\x08J\x04\x08\x08\x10\tR\x08team_idsR\x12\x61gent_principal_id\"\\\n\x12InstallationNotice\x12\x46\n\x0cinstallation\x18\x01 \x01(\x0b\x32\".host.installation.v1.InstallationR\x0cinstallation\"\x11\n\x0fInstallationAck\"<\n\x11ListOffersRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\"I\n\x12ListOffersResponse\x12\x33\n\x06offers\x18\x01 \x03(\x0b\x32\x1b.host.installation.v1.OfferR\x06offers\"?\n\x05Offer\x12\x16\n\x06target\x18\x01 \x01(\tR\x06target\x12\x1e\n\ngeneration\x18\x02 \x01(\x04R\ngeneration\"\xcf\x03\n\rReviewedOffer\x12\x41\n\x08\x61pproval\x18\x01 \x01(\x0b\x32%.host.installation.v1.ConsentApprovalR\x08\x61pproval\x12\x16\n\x06target\x18\x02 \x01(\tR\x06target\x12\x35\n\x05roles\x18\x03 \x03(\x0b\x32\x1f.host.installation.v1.OfferRoleR\x05roles\x12\x46\n\x0cinstallation\x18\x04 \x01(\x0b\x32\".host.installation.v1.InstallationR\x0cinstallation\x12<\n\x07\x63\x65iling\x18\x05 \x03(\x0b\x32\".host.installation.v1.OfferBindingR\x07\x63\x65iling\x12%\n\x0etools_composed\x18\x06 \x01(\x08R\rtoolsComposed\x12+\n\x11installation_role\x18\x07 \x01(\tR\x10installationRole\x12R\n\x11\x65\x64ge_dependencies\x18\x08 \x03(\x0b\x32%.host.installation.v1.OfferDependencyR\x10\x65\x64geDependencies\"E\n\x0fOfferDependency\x12\x16\n\x06module\x18\x01 \x01(\tR\x06module\x12\x1a\n\x08presence\x18\x02 \x01(\tR\x08presence\"z\n\x0cOfferBinding\x12\x18\n\x07\x62inding\x18\x01 \x01(\tR\x07\x62inding\x12\x16\n\x06module\x18\x02 \x01(\tR\x06module\x12\x38\n\x06scopes\x18\x03 \x03(\x0b\x32 .host.installation.v1.OfferScopeR\x06scopes\"\xa4\x01\n\tOfferRole\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x38\n\x06scopes\x18\x02 \x03(\x0b\x32 .host.installation.v1.OfferScopeR\x06scopes\x12I\n\x0c\x63\x61pabilities\x18\x03 \x03(\x0b\x32%.host.installation.v1.OfferCapabilityR\x0c\x63\x61pabilities\"\x93\x01\n\nOfferScope\x12#\n\rresource_kind\x18\x01 \x01(\tR\x0cresourceKind\x12\x18\n\x07\x61\x63tions\x18\x02 \x03(\tR\x07\x61\x63tions\x12#\n\rattached_only\x18\x03 \x01(\x08R\x0c\x61ttachedOnly\x12!\n\x0cresource_ids\x18\x04 \x03(\tR\x0bresourceIds\"\xbc\x01\n\x0fOfferCapability\x12\x16\n\x06module\x18\x01 \x01(\tR\x06module\x12\x1c\n\toperation\x18\x02 \x01(\tR\toperation\x12<\n\x08requires\x18\x03 \x03(\x0b\x32 .host.installation.v1.OfferScopeR\x08requires\x12\x35\n\x05tools\x18\x04 \x03(\x0b\x32\x1f.host.installation.v1.OfferToolR\x05tools\"O\n\tOfferTool\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n\x07\x62inding\x18\x02 \x01(\tR\x07\x62inding\x12\x14\n\x05write\x18\x03 \x01(\x08R\x05write\":\n\x0fListHeldRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\"`\n\x10ListHeldResponse\x12L\n\rinstallations\x18\x01 \x03(\x0b\x32&.host.installation.v1.HeldInstallationR\rinstallations\"p\n\x10HeldInstallation\x12\x46\n\x0cinstallation\x18\x01 \x01(\x0b\x32\".host.installation.v1.InstallationR\x0cinstallation\x12\x14\n\x05roles\x18\x02 \x03(\tR\x05roles2\xf3\x05\n\x13InstallationService\x12\x66\n\x07\x43urrent\x12\x30.host.installation.v1.CurrentInstallationRequest\x1a).host.installation.v1.CurrentInstallation\x12S\n\x07Install\x12$.host.installation.v1.InstallRequest\x1a\".host.installation.v1.Installation\x12T\n\x06Review\x12#.host.installation.v1.ReviewRequest\x1a%.host.installation.v1.ConsentApproval\x12W\n\tGrantRole\x12&.host.installation.v1.GrantRoleRequest\x1a\".host.installation.v1.Installation\x12Y\n\nRevokeRole\x12\'.host.installation.v1.RevokeRoleRequest\x1a\".host.installation.v1.Installation\x12k\n\x0eListRoleGrants\x12+.host.installation.v1.ListRoleGrantsRequest\x1a,.host.installation.v1.ListRoleGrantsResponse\x12M\n\x04List\x12!.host.installation.v1.ListRequest\x1a\".host.installation.v1.ListResponse\x12Y\n\x08ListHeld\x12%.host.installation.v1.ListHeldRequest\x1a&.host.installation.v1.ListHeldResponse2p\n\x12InstallableService\x12Z\n\x07Install\x12(.host.installation.v1.InstallationNotice\x1a%.host.installation.v1.InstallationAck2\xc8\x01\n\x0cOfferService\x12_\n\nListOffers\x12\'.host.installation.v1.ListOffersRequest\x1a(.host.installation.v1.ListOffersResponse\x12W\n\x0bReviewOffer\x12#.host.installation.v1.ReviewRequest\x1a#.host.installation.v1.ReviewedOfferB5Z3gateway/pkg/gen/host/installation/v1;installationv1b\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -71,27 +71,29 @@ _globals['_OFFER']._serialized_start=2277 _globals['_OFFER']._serialized_end=2340 _globals['_REVIEWEDOFFER']._serialized_start=2343 - _globals['_REVIEWEDOFFER']._serialized_end=2677 - _globals['_OFFERBINDING']._serialized_start=2679 - _globals['_OFFERBINDING']._serialized_end=2801 - _globals['_OFFERROLE']._serialized_start=2804 - _globals['_OFFERROLE']._serialized_end=2968 - _globals['_OFFERSCOPE']._serialized_start=2971 - _globals['_OFFERSCOPE']._serialized_end=3118 - _globals['_OFFERCAPABILITY']._serialized_start=3121 - _globals['_OFFERCAPABILITY']._serialized_end=3309 - _globals['_OFFERTOOL']._serialized_start=3311 - _globals['_OFFERTOOL']._serialized_end=3390 - _globals['_LISTHELDREQUEST']._serialized_start=3392 - _globals['_LISTHELDREQUEST']._serialized_end=3450 - _globals['_LISTHELDRESPONSE']._serialized_start=3452 - _globals['_LISTHELDRESPONSE']._serialized_end=3548 - _globals['_HELDINSTALLATION']._serialized_start=3550 - _globals['_HELDINSTALLATION']._serialized_end=3662 - _globals['_INSTALLATIONSERVICE']._serialized_start=3665 - _globals['_INSTALLATIONSERVICE']._serialized_end=4420 - _globals['_INSTALLABLESERVICE']._serialized_start=4422 - _globals['_INSTALLABLESERVICE']._serialized_end=4534 - _globals['_OFFERSERVICE']._serialized_start=4537 - _globals['_OFFERSERVICE']._serialized_end=4737 + _globals['_REVIEWEDOFFER']._serialized_end=2806 + _globals['_OFFERDEPENDENCY']._serialized_start=2808 + _globals['_OFFERDEPENDENCY']._serialized_end=2877 + _globals['_OFFERBINDING']._serialized_start=2879 + _globals['_OFFERBINDING']._serialized_end=3001 + _globals['_OFFERROLE']._serialized_start=3004 + _globals['_OFFERROLE']._serialized_end=3168 + _globals['_OFFERSCOPE']._serialized_start=3171 + _globals['_OFFERSCOPE']._serialized_end=3318 + _globals['_OFFERCAPABILITY']._serialized_start=3321 + _globals['_OFFERCAPABILITY']._serialized_end=3509 + _globals['_OFFERTOOL']._serialized_start=3511 + _globals['_OFFERTOOL']._serialized_end=3590 + _globals['_LISTHELDREQUEST']._serialized_start=3592 + _globals['_LISTHELDREQUEST']._serialized_end=3650 + _globals['_LISTHELDRESPONSE']._serialized_start=3652 + _globals['_LISTHELDRESPONSE']._serialized_end=3748 + _globals['_HELDINSTALLATION']._serialized_start=3750 + _globals['_HELDINSTALLATION']._serialized_end=3862 + _globals['_INSTALLATIONSERVICE']._serialized_start=3865 + _globals['_INSTALLATIONSERVICE']._serialized_end=4620 + _globals['_INSTALLABLESERVICE']._serialized_start=4622 + _globals['_INSTALLABLESERVICE']._serialized_end=4734 + _globals['_OFFERSERVICE']._serialized_start=4737 + _globals['_OFFERSERVICE']._serialized_end=4937 # @@protoc_insertion_point(module_scope) diff --git a/libraries/authority-client/typescript/src/gen/host/authority/v1/authority_pb.ts b/libraries/authority-client/typescript/src/gen/host/authority/v1/authority_pb.ts index 02f1096a..e9322b1b 100644 --- a/libraries/authority-client/typescript/src/gen/host/authority/v1/authority_pb.ts +++ b/libraries/authority-client/typescript/src/gen/host/authority/v1/authority_pb.ts @@ -462,6 +462,15 @@ export type CheckRequest = Message<"host.authority.v1.CheckRequest"> & { * applies what the audience module's delivery declares for that method, and * refuses by name a call that does not hold it, a method that declares * nothing, and a method the module does not serve. + * Two exact methods are reserved for installation setup instead of delivered + * method policy: "POST /_codefly/installations" and + * "/host.installation.v1.InstallableService/Install". They accept only the + * setup-only credential for the exact current consented pending installation; + * ordinary capabilities are refused, even if delivered policy permits them. + * The receiver supplies its actual method and its own delivered audience, + * never values selected by the request body. The verified organization and + * installation seal must match the setup body and carriers before callback + * execution or a cached acknowledgement. Setup admits no ordinary data work. * * @generated from field: string method = 3; */ @@ -474,6 +483,8 @@ export type CheckRequest = Message<"host.authority.v1.CheckRequest"> & { * capability is answered. A hop in a composition that runs permissive * (hop/ENFORCEMENT=warn) asks this way and logs the refusal it did not make. * Hosts may refuse advisory outside local development runs. + * At either reserved setup method, advisory does not relax setup credential + * authentication or its pending-attempt checks: a refusal is always an error. * * @generated from field: bool advisory = 4; */ diff --git a/libraries/authority-client/typescript/src/gen/host/composition/v1/composition_pb.ts b/libraries/authority-client/typescript/src/gen/host/composition/v1/composition_pb.ts index 37f58c04..569c301f 100644 --- a/libraries/authority-client/typescript/src/gen/host/composition/v1/composition_pb.ts +++ b/libraries/authority-client/typescript/src/gen/host/composition/v1/composition_pb.ts @@ -17,7 +17,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file host/composition/v1/composition.proto. */ export const file_host_composition_v1_composition: GenFile = /*@__PURE__*/ - fileDesc("CiVob3N0L2NvbXBvc2l0aW9uL3YxL2NvbXBvc2l0aW9uLnByb3RvEhNob3N0LmNvbXBvc2l0aW9uLnYxItUBChRBcHBseU1hbmlmZXN0UmVxdWVzdBI3CghtYW5pZmVzdBgBIAEoCzIlLmhvc3QuY29tcG9zaXRpb24udjEuTW9kdWxlTWFuaWZlc3RWMRI2CgdjYXJyaWVyGAIgASgLMiUuaG9zdC5jb21wb3NpdGlvbi52MS5TaWduZWRNYW5pZmVzdFYxEhIKCmNyZWRlbnRpYWwYAyABKAkSOAoJYXV0aG9yaXR5GAQgASgLMiUuaG9zdC5jb21wb3NpdGlvbi52MS5TaWduZWRNYW5pZmVzdFYxIoYCChNBdXRob3JpdHlNYW5pZmVzdFYxEg4KBm1vZHVsZRgBIAEoCRISCgpnZW5lcmF0aW9uGAIgASgEEhwKFGNvcmVfbWFuaWZlc3RfZGlnZXN0GAMgASgJEjQKB21ldGhvZHMYBCADKAsyIy5ob3N0LmNvbXBvc2l0aW9uLnYxLk1ldGhvZFBvbGljeVYxEjYKDnNjb3BlX2NlaWxpbmdzGAUgAygLMh4uaG9zdC5hdXRob3JpdHkudjEuV29ya1Njb3BlVjESKgoFcm9sZXMYBiADKAsyGy5ob3N0LmNvbXBvc2l0aW9uLnYxLlJvbGVWMRITCgtvd25lcl9hd2FyZRgHIAEoCCJuCg5NZXRob2RQb2xpY3lWMRIOCgZtZXRob2QYASABKAkSMAoIcmVxdWlyZXMYAiADKAsyHi5ob3N0LmF1dGhvcml0eS52MS5Xb3JrU2NvcGVWMRIaChJhbnlfY2hlY2tlZF9jYWxsZXIYAyABKAgiNgoQU2lnbmVkTWFuaWZlc3RWMRIQCghkb2N1bWVudBgBIAEoDBIQCghlbnZlbG9wZRgCIAEoDCJYChVBcHBseU1hbmlmZXN0UmVzcG9uc2USEQoJcHJpbmNpcGFsGAEgASgJEhIKCmdlbmVyYXRpb24YAiABKAQSGAoQaW5zdGFsbGF0aW9uX2lkcxgDIAMoCSKdAgoQTW9kdWxlTWFuaWZlc3RWMRIOCgZtb2R1bGUYASABKAkSEQoJcHJpbmNpcGFsGAIgASgJEgwKBGtpbmQYAyABKAkSEgoKZ2VuZXJhdGlvbhgEIAEoBBIwCghiaW5kaW5ncxgFIAMoCzIeLmhvc3QuY29tcG9zaXRpb24udjEuQmluZGluZ1YxEjsKDnJlc291cmNlX2tpbmRzGAYgAygLMiMuaG9zdC5jb21wb3NpdGlvbi52MS5SZXNvdXJjZUtpbmRWMRIpCgRwYWdlGAcgASgLMhsuaG9zdC5jb21wb3NpdGlvbi52MS5QYWdlVjESKgoFcm9sZXMYCCADKAsyGy5ob3N0LmNvbXBvc2l0aW9uLnYxLlJvbGVWMSKJAQoGUm9sZVYxEgwKBG5hbWUYASABKAkSLgoGc2NvcGVzGAIgAygLMh4uaG9zdC5hdXRob3JpdHkudjEuV29ya1Njb3BlVjESQQoZaW5zdGFsbGF0aW9uX293bmVkX3Njb3BlcxgDIAMoCzIeLmhvc3QuYXV0aG9yaXR5LnYxLldvcmtTY29wZVYxItABCglCaW5kaW5nVjESCgoCaWQYASABKAkSEAoIYXVkaWVuY2UYAiABKAkSNQoNaW52b2tlX3Njb3BlcxgDIAMoCzIeLmhvc3QuYXV0aG9yaXR5LnYxLldvcmtTY29wZVYxEjcKD2hlYWRsZXNzX3Njb3BlcxgEIAMoCzIeLmhvc3QuYXV0aG9yaXR5LnYxLldvcmtTY29wZVYxEjUKDWxvb2t1cF9zY29wZXMYBSADKAsyHi5ob3N0LmF1dGhvcml0eS52MS5Xb3JrU2NvcGVWMSIvCg5SZXNvdXJjZUtpbmRWMRIMCgRraW5kGAEgASgJEg8KB2FjdGlvbnMYAiADKAkiJgoGUGFnZVYxEg0KBXRpdGxlGAEgASgJEg0KBW9yZGVyGAIgASgFMnwKEkNvbXBvc2l0aW9uU2VydmljZRJmCg1BcHBseU1hbmlmZXN0EikuaG9zdC5jb21wb3NpdGlvbi52MS5BcHBseU1hbmlmZXN0UmVxdWVzdBoqLmhvc3QuY29tcG9zaXRpb24udjEuQXBwbHlNYW5pZmVzdFJlc3BvbnNlQjNaMWdhdGV3YXkvcGtnL2dlbi9ob3N0L2NvbXBvc2l0aW9uL3YxO2NvbXBvc2l0aW9udjFiBnByb3RvMw", [file_host_authority_v1_work_context]); + fileDesc("CiVob3N0L2NvbXBvc2l0aW9uL3YxL2NvbXBvc2l0aW9uLnByb3RvEhNob3N0LmNvbXBvc2l0aW9uLnYxItUBChRBcHBseU1hbmlmZXN0UmVxdWVzdBI3CghtYW5pZmVzdBgBIAEoCzIlLmhvc3QuY29tcG9zaXRpb24udjEuTW9kdWxlTWFuaWZlc3RWMRI2CgdjYXJyaWVyGAIgASgLMiUuaG9zdC5jb21wb3NpdGlvbi52MS5TaWduZWRNYW5pZmVzdFYxEhIKCmNyZWRlbnRpYWwYAyABKAkSOAoJYXV0aG9yaXR5GAQgASgLMiUuaG9zdC5jb21wb3NpdGlvbi52MS5TaWduZWRNYW5pZmVzdFYxIuMCChNBdXRob3JpdHlNYW5pZmVzdFYxEg4KBm1vZHVsZRgBIAEoCRISCgpnZW5lcmF0aW9uGAIgASgEEhwKFGNvcmVfbWFuaWZlc3RfZGlnZXN0GAMgASgJEjQKB21ldGhvZHMYBCADKAsyIy5ob3N0LmNvbXBvc2l0aW9uLnYxLk1ldGhvZFBvbGljeVYxEjYKDnNjb3BlX2NlaWxpbmdzGAUgAygLMh4uaG9zdC5hdXRob3JpdHkudjEuV29ya1Njb3BlVjESKgoFcm9sZXMYBiADKAsyGy5ob3N0LmNvbXBvc2l0aW9uLnYxLlJvbGVWMRITCgtvd25lcl9hd2FyZRgHIAEoCBIZChFpbnN0YWxsYXRpb25fcm9sZRgIIAEoCRJAChFlZGdlX2RlcGVuZGVuY2llcxgJIAMoCzIlLmhvc3QuY29tcG9zaXRpb24udjEuRWRnZURlcGVuZGVuY3lWMSI0ChBFZGdlRGVwZW5kZW5jeVYxEg4KBm1vZHVsZRgBIAEoCRIQCghwcmVzZW5jZRgCIAEoCSJuCg5NZXRob2RQb2xpY3lWMRIOCgZtZXRob2QYASABKAkSMAoIcmVxdWlyZXMYAiADKAsyHi5ob3N0LmF1dGhvcml0eS52MS5Xb3JrU2NvcGVWMRIaChJhbnlfY2hlY2tlZF9jYWxsZXIYAyABKAgiNgoQU2lnbmVkTWFuaWZlc3RWMRIQCghkb2N1bWVudBgBIAEoDBIQCghlbnZlbG9wZRgCIAEoDCJYChVBcHBseU1hbmlmZXN0UmVzcG9uc2USEQoJcHJpbmNpcGFsGAEgASgJEhIKCmdlbmVyYXRpb24YAiABKAQSGAoQaW5zdGFsbGF0aW9uX2lkcxgDIAMoCSKdAgoQTW9kdWxlTWFuaWZlc3RWMRIOCgZtb2R1bGUYASABKAkSEQoJcHJpbmNpcGFsGAIgASgJEgwKBGtpbmQYAyABKAkSEgoKZ2VuZXJhdGlvbhgEIAEoBBIwCghiaW5kaW5ncxgFIAMoCzIeLmhvc3QuY29tcG9zaXRpb24udjEuQmluZGluZ1YxEjsKDnJlc291cmNlX2tpbmRzGAYgAygLMiMuaG9zdC5jb21wb3NpdGlvbi52MS5SZXNvdXJjZUtpbmRWMRIpCgRwYWdlGAcgASgLMhsuaG9zdC5jb21wb3NpdGlvbi52MS5QYWdlVjESKgoFcm9sZXMYCCADKAsyGy5ob3N0LmNvbXBvc2l0aW9uLnYxLlJvbGVWMSKJAQoGUm9sZVYxEgwKBG5hbWUYASABKAkSLgoGc2NvcGVzGAIgAygLMh4uaG9zdC5hdXRob3JpdHkudjEuV29ya1Njb3BlVjESQQoZaW5zdGFsbGF0aW9uX293bmVkX3Njb3BlcxgDIAMoCzIeLmhvc3QuYXV0aG9yaXR5LnYxLldvcmtTY29wZVYxItABCglCaW5kaW5nVjESCgoCaWQYASABKAkSEAoIYXVkaWVuY2UYAiABKAkSNQoNaW52b2tlX3Njb3BlcxgDIAMoCzIeLmhvc3QuYXV0aG9yaXR5LnYxLldvcmtTY29wZVYxEjcKD2hlYWRsZXNzX3Njb3BlcxgEIAMoCzIeLmhvc3QuYXV0aG9yaXR5LnYxLldvcmtTY29wZVYxEjUKDWxvb2t1cF9zY29wZXMYBSADKAsyHi5ob3N0LmF1dGhvcml0eS52MS5Xb3JrU2NvcGVWMSIvCg5SZXNvdXJjZUtpbmRWMRIMCgRraW5kGAEgASgJEg8KB2FjdGlvbnMYAiADKAkiJgoGUGFnZVYxEg0KBXRpdGxlGAEgASgJEg0KBW9yZGVyGAIgASgFMnwKEkNvbXBvc2l0aW9uU2VydmljZRJmCg1BcHBseU1hbmlmZXN0EikuaG9zdC5jb21wb3NpdGlvbi52MS5BcHBseU1hbmlmZXN0UmVxdWVzdBoqLmhvc3QuY29tcG9zaXRpb24udjEuQXBwbHlNYW5pZmVzdFJlc3BvbnNlQjNaMWdhdGV3YXkvcGtnL2dlbi9ob3N0L2NvbXBvc2l0aW9uL3YxO2NvbXBvc2l0aW9udjFiBnByb3RvMw", [file_host_authority_v1_work_context]); /** * ApplyManifestRequest carries the manifest. @@ -139,6 +139,21 @@ export type AuthorityManifestV1 = Message<"host.composition.v1.AuthorityManifest * @generated from field: bool owner_aware = 7; */ ownerAware: boolean; + + /** + * Core-validated selector, carried verbatim: empty is legacy caller selection, + * "none" declares no standing machine authority, otherwise a declared role. + * + * @generated from field: string installation_role = 8; + */ + installationRole: string; + + /** + * Viewer-edge presence only; grants no authority. Nil and empty are equivalent. + * + * @generated from field: repeated host.composition.v1.EdgeDependencyV1 edge_dependencies = 9; + */ + edgeDependencies: EdgeDependencyV1[]; }; /** @@ -148,6 +163,33 @@ export type AuthorityManifestV1 = Message<"host.composition.v1.AuthorityManifest export const AuthorityManifestV1Schema: GenMessage = /*@__PURE__*/ messageDesc(file_host_composition_v1_composition, 1); +/** + * EdgeDependencyV1 names the exact signed module principal. The host resolves + * it to a live catalogue target; it is not a target UUID, route alias or binding id. + * + * @generated from message host.composition.v1.EdgeDependencyV1 + */ +export type EdgeDependencyV1 = Message<"host.composition.v1.EdgeDependencyV1"> & { + /** + * @generated from field: string module = 1; + */ + module: string; + + /** + * Core validates required or optional; delivery does not default this value. + * + * @generated from field: string presence = 2; + */ + presence: string; +}; + +/** + * Describes the message host.composition.v1.EdgeDependencyV1. + * Use `create(EdgeDependencyV1Schema)` to create a new message. + */ +export const EdgeDependencyV1Schema: GenMessage = /*@__PURE__*/ + messageDesc(file_host_composition_v1_composition, 2); + /** * MethodPolicyV1 is what a call to one method must hold, as the module's * contract declares it with the authority.hop.v1.policy option. @@ -184,7 +226,7 @@ export type MethodPolicyV1 = Message<"host.composition.v1.MethodPolicyV1"> & { * Use `create(MethodPolicyV1Schema)` to create a new message. */ export const MethodPolicyV1Schema: GenMessage = /*@__PURE__*/ - messageDesc(file_host_composition_v1_composition, 2); + messageDesc(file_host_composition_v1_composition, 3); /** * SignedManifestV1 is core's signed carrier: the manifest document (JSON) and @@ -209,7 +251,7 @@ export type SignedManifestV1 = Message<"host.composition.v1.SignedManifestV1"> & * Use `create(SignedManifestV1Schema)` to create a new message. */ export const SignedManifestV1Schema: GenMessage = /*@__PURE__*/ - messageDesc(file_host_composition_v1_composition, 3); + messageDesc(file_host_composition_v1_composition, 4); /** * ApplyManifestResponse says what the host derived. @@ -244,7 +286,7 @@ export type ApplyManifestResponse = Message<"host.composition.v1.ApplyManifestRe * Use `create(ApplyManifestResponseSchema)` to create a new message. */ export const ApplyManifestResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_host_composition_v1_composition, 4); + messageDesc(file_host_composition_v1_composition, 5); /** * ModuleManifestV1 is one module instance at one generation. @@ -317,7 +359,7 @@ export type ModuleManifestV1 = Message<"host.composition.v1.ModuleManifestV1"> & * Use `create(ModuleManifestV1Schema)` to create a new message. */ export const ModuleManifestV1Schema: GenMessage = /*@__PURE__*/ - messageDesc(file_host_composition_v1_composition, 5); + messageDesc(file_host_composition_v1_composition, 6); /** * RoleV1 is one role a solution declares. @@ -355,7 +397,7 @@ export type RoleV1 = Message<"host.composition.v1.RoleV1"> & { * Use `create(RoleV1Schema)` to create a new message. */ export const RoleV1Schema: GenMessage = /*@__PURE__*/ - messageDesc(file_host_composition_v1_composition, 6); + messageDesc(file_host_composition_v1_composition, 7); /** * BindingV1 is one declared call towards another module. @@ -407,7 +449,7 @@ export type BindingV1 = Message<"host.composition.v1.BindingV1"> & { * Use `create(BindingV1Schema)` to create a new message. */ export const BindingV1Schema: GenMessage = /*@__PURE__*/ - messageDesc(file_host_composition_v1_composition, 7); + messageDesc(file_host_composition_v1_composition, 8); /** * ResourceKindV1 is one kind the module governs and the actions on it. @@ -435,7 +477,7 @@ export type ResourceKindV1 = Message<"host.composition.v1.ResourceKindV1"> & { * Use `create(ResourceKindV1Schema)` to create a new message. */ export const ResourceKindV1Schema: GenMessage = /*@__PURE__*/ - messageDesc(file_host_composition_v1_composition, 8); + messageDesc(file_host_composition_v1_composition, 9); /** * PageV1 is a module's frontend contribution. @@ -463,7 +505,7 @@ export type PageV1 = Message<"host.composition.v1.PageV1"> & { * Use `create(PageV1Schema)` to create a new message. */ export const PageV1Schema: GenMessage = /*@__PURE__*/ - messageDesc(file_host_composition_v1_composition, 9); + messageDesc(file_host_composition_v1_composition, 10); /** * CompositionService is implemented by the host and by the local authority. diff --git a/libraries/authority-client/typescript/src/gen/host/installation/v1/installation_pb.ts b/libraries/authority-client/typescript/src/gen/host/installation/v1/installation_pb.ts index ee093729..6c7d8894 100644 --- a/libraries/authority-client/typescript/src/gen/host/installation/v1/installation_pb.ts +++ b/libraries/authority-client/typescript/src/gen/host/installation/v1/installation_pb.ts @@ -16,7 +16,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file host/installation/v1/installation.proto. */ export const file_host_installation_v1_installation: GenFile = /*@__PURE__*/ - fileDesc("Cidob3N0L2luc3RhbGxhdGlvbi92MS9pbnN0YWxsYXRpb24ucHJvdG8SFGhvc3QuaW5zdGFsbGF0aW9uLnYxIkwKGkN1cnJlbnRJbnN0YWxsYXRpb25SZXF1ZXN0EhQKDHBhcmVudF90b2tlbhgBIAEoCRIYChBhY3Rvcl9jcmVkZW50aWFsGAIgASgJIm8KE0N1cnJlbnRJbnN0YWxsYXRpb24SCgoCaWQYASABKAkSFwoPb3JnYW5pemF0aW9uX2lkGAIgASgJEg4KBnRhcmdldBgDIAEoCRIQCghyZXZpc2lvbhgEIAEoBBIRCgluYW1lc3BhY2UYBSABKAkimQEKDkluc3RhbGxSZXF1ZXN0EhcKD29yZ2FuaXphdGlvbl9pZBgBIAEoCRIOCgZ0YXJnZXQYAiABKAkSDAoEcm9sZRgDIAEoCRIXCg9vd25lcl9wZXJzb25faWQYBCABKAkSNwoIYXBwcm92YWwYBSABKAsyJS5ob3N0Lmluc3RhbGxhdGlvbi52MS5Db25zZW50QXBwcm92YWwiOAoNUmV2aWV3UmVxdWVzdBIXCg9vcmdhbml6YXRpb25faWQYASABKAkSDgoGdGFyZ2V0GAIgASgJInkKD0NvbnNlbnRBcHByb3ZhbBIRCgl0YXJnZXRfaWQYASABKAkSEgoKZ2VuZXJhdGlvbhgCIAEoBBIZChFleHBlY3RlZF9yZXZpc2lvbhgDIAEoBBIUCgxvcGVyYXRpb25faWQYBCABKAkSDgoGZGlnZXN0GAUgASgJInYKEEdyYW50Um9sZVJlcXVlc3QSFwoPb3JnYW5pemF0aW9uX2lkGAEgASgJEhcKD2luc3RhbGxhdGlvbl9pZBgCIAEoCRIMCgRyb2xlGAMgASgJEhEKCXBlcnNvbl9pZBgEIAEoCRIPCgd0ZWFtX2lkGAUgASgJIncKEVJldm9rZVJvbGVSZXF1ZXN0EhcKD29yZ2FuaXphdGlvbl9pZBgBIAEoCRIXCg9pbnN0YWxsYXRpb25faWQYAiABKAkSDAoEcm9sZRgDIAEoCRIRCglwZXJzb25faWQYBCABKAkSDwoHdGVhbV9pZBgFIAEoCSJJChVMaXN0Um9sZUdyYW50c1JlcXVlc3QSFwoPb3JnYW5pemF0aW9uX2lkGAEgASgJEhcKD2luc3RhbGxhdGlvbl9pZBgCIAEoCSJJChZMaXN0Um9sZUdyYW50c1Jlc3BvbnNlEi8KBmdyYW50cxgBIAMoCzIfLmhvc3QuaW5zdGFsbGF0aW9uLnYxLlJvbGVHcmFudCI9CglSb2xlR3JhbnQSDAoEcm9sZRgBIAEoCRIRCglwZXJzb25faWQYAiABKAkSDwoHdGVhbV9pZBgDIAEoCSImCgtMaXN0UmVxdWVzdBIXCg9vcmdhbml6YXRpb25faWQYASABKAkiSQoMTGlzdFJlc3BvbnNlEjkKDWluc3RhbGxhdGlvbnMYASADKAsyIi5ob3N0Lmluc3RhbGxhdGlvbi52MS5JbnN0YWxsYXRpb24i3QIKDEluc3RhbGxhdGlvbhIKCgJpZBgBIAEoCRIXCg9vcmdhbml6YXRpb25faWQYAiABKAkSDgoGdGFyZ2V0GAMgASgJEjkKBnN0YXR1cxgEIAEoDjIpLmhvc3QuaW5zdGFsbGF0aW9uLnYxLkluc3RhbGxhdGlvbi5TdGF0dXMSEAoIcmV2aXNpb24YBSABKAQSDAoEcm9sZRgGIAEoCRIRCgluYW1lc3BhY2UYCSABKAkSFAoMcHJpbmNpcGFsX2lkGAogASgJEg0KBXJvbGVzGAsgAygJIlsKBlN0YXR1cxIWChJTVEFUVVNfVU5TUEVDSUZJRUQQABISCg5TVEFUVVNfUEVORElORxABEhEKDVNUQVRVU19BQ1RJVkUQAhISCg5TVEFUVVNfUkVWT0tFRBADSgQIBxAISgQICBAJUgh0ZWFtX2lkc1ISYWdlbnRfcHJpbmNpcGFsX2lkIk4KEkluc3RhbGxhdGlvbk5vdGljZRI4CgxpbnN0YWxsYXRpb24YASABKAsyIi5ob3N0Lmluc3RhbGxhdGlvbi52MS5JbnN0YWxsYXRpb24iEQoPSW5zdGFsbGF0aW9uQWNrIiwKEUxpc3RPZmZlcnNSZXF1ZXN0EhcKD29yZ2FuaXphdGlvbl9pZBgBIAEoCSJBChJMaXN0T2ZmZXJzUmVzcG9uc2USKwoGb2ZmZXJzGAEgAygLMhsuaG9zdC5pbnN0YWxsYXRpb24udjEuT2ZmZXIiKwoFT2ZmZXISDgoGdGFyZ2V0GAEgASgJEhIKCmdlbmVyYXRpb24YAiABKAQijwIKDVJldmlld2VkT2ZmZXISNwoIYXBwcm92YWwYASABKAsyJS5ob3N0Lmluc3RhbGxhdGlvbi52MS5Db25zZW50QXBwcm92YWwSDgoGdGFyZ2V0GAIgASgJEi4KBXJvbGVzGAMgAygLMh8uaG9zdC5pbnN0YWxsYXRpb24udjEuT2ZmZXJSb2xlEjgKDGluc3RhbGxhdGlvbhgEIAEoCzIiLmhvc3QuaW5zdGFsbGF0aW9uLnYxLkluc3RhbGxhdGlvbhIzCgdjZWlsaW5nGAUgAygLMiIuaG9zdC5pbnN0YWxsYXRpb24udjEuT2ZmZXJCaW5kaW5nEhYKDnRvb2xzX2NvbXBvc2VkGAYgASgIImEKDE9mZmVyQmluZGluZxIPCgdiaW5kaW5nGAEgASgJEg4KBm1vZHVsZRgCIAEoCRIwCgZzY29wZXMYAyADKAsyIC5ob3N0Lmluc3RhbGxhdGlvbi52MS5PZmZlclNjb3BlIogBCglPZmZlclJvbGUSDAoEbmFtZRgBIAEoCRIwCgZzY29wZXMYAiADKAsyIC5ob3N0Lmluc3RhbGxhdGlvbi52MS5PZmZlclNjb3BlEjsKDGNhcGFiaWxpdGllcxgDIAMoCzIlLmhvc3QuaW5zdGFsbGF0aW9uLnYxLk9mZmVyQ2FwYWJpbGl0eSJhCgpPZmZlclNjb3BlEhUKDXJlc291cmNlX2tpbmQYASABKAkSDwoHYWN0aW9ucxgCIAMoCRIVCg1hdHRhY2hlZF9vbmx5GAMgASgIEhQKDHJlc291cmNlX2lkcxgEIAMoCSKYAQoPT2ZmZXJDYXBhYmlsaXR5Eg4KBm1vZHVsZRgBIAEoCRIRCglvcGVyYXRpb24YAiABKAkSMgoIcmVxdWlyZXMYAyADKAsyIC5ob3N0Lmluc3RhbGxhdGlvbi52MS5PZmZlclNjb3BlEi4KBXRvb2xzGAQgAygLMh8uaG9zdC5pbnN0YWxsYXRpb24udjEuT2ZmZXJUb29sIjkKCU9mZmVyVG9vbBIMCgRuYW1lGAEgASgJEg8KB2JpbmRpbmcYAiABKAkSDQoFd3JpdGUYAyABKAgiKgoPTGlzdEhlbGRSZXF1ZXN0EhcKD29yZ2FuaXphdGlvbl9pZBgBIAEoCSJRChBMaXN0SGVsZFJlc3BvbnNlEj0KDWluc3RhbGxhdGlvbnMYASADKAsyJi5ob3N0Lmluc3RhbGxhdGlvbi52MS5IZWxkSW5zdGFsbGF0aW9uIlsKEEhlbGRJbnN0YWxsYXRpb24SOAoMaW5zdGFsbGF0aW9uGAEgASgLMiIuaG9zdC5pbnN0YWxsYXRpb24udjEuSW5zdGFsbGF0aW9uEg0KBXJvbGVzGAIgAygJMvMFChNJbnN0YWxsYXRpb25TZXJ2aWNlEmYKB0N1cnJlbnQSMC5ob3N0Lmluc3RhbGxhdGlvbi52MS5DdXJyZW50SW5zdGFsbGF0aW9uUmVxdWVzdBopLmhvc3QuaW5zdGFsbGF0aW9uLnYxLkN1cnJlbnRJbnN0YWxsYXRpb24SUwoHSW5zdGFsbBIkLmhvc3QuaW5zdGFsbGF0aW9uLnYxLkluc3RhbGxSZXF1ZXN0GiIuaG9zdC5pbnN0YWxsYXRpb24udjEuSW5zdGFsbGF0aW9uElQKBlJldmlldxIjLmhvc3QuaW5zdGFsbGF0aW9uLnYxLlJldmlld1JlcXVlc3QaJS5ob3N0Lmluc3RhbGxhdGlvbi52MS5Db25zZW50QXBwcm92YWwSVwoJR3JhbnRSb2xlEiYuaG9zdC5pbnN0YWxsYXRpb24udjEuR3JhbnRSb2xlUmVxdWVzdBoiLmhvc3QuaW5zdGFsbGF0aW9uLnYxLkluc3RhbGxhdGlvbhJZCgpSZXZva2VSb2xlEicuaG9zdC5pbnN0YWxsYXRpb24udjEuUmV2b2tlUm9sZVJlcXVlc3QaIi5ob3N0Lmluc3RhbGxhdGlvbi52MS5JbnN0YWxsYXRpb24SawoOTGlzdFJvbGVHcmFudHMSKy5ob3N0Lmluc3RhbGxhdGlvbi52MS5MaXN0Um9sZUdyYW50c1JlcXVlc3QaLC5ob3N0Lmluc3RhbGxhdGlvbi52MS5MaXN0Um9sZUdyYW50c1Jlc3BvbnNlEk0KBExpc3QSIS5ob3N0Lmluc3RhbGxhdGlvbi52MS5MaXN0UmVxdWVzdBoiLmhvc3QuaW5zdGFsbGF0aW9uLnYxLkxpc3RSZXNwb25zZRJZCghMaXN0SGVsZBIlLmhvc3QuaW5zdGFsbGF0aW9uLnYxLkxpc3RIZWxkUmVxdWVzdBomLmhvc3QuaW5zdGFsbGF0aW9uLnYxLkxpc3RIZWxkUmVzcG9uc2UycAoSSW5zdGFsbGFibGVTZXJ2aWNlEloKB0luc3RhbGwSKC5ob3N0Lmluc3RhbGxhdGlvbi52MS5JbnN0YWxsYXRpb25Ob3RpY2UaJS5ob3N0Lmluc3RhbGxhdGlvbi52MS5JbnN0YWxsYXRpb25BY2syyAEKDE9mZmVyU2VydmljZRJfCgpMaXN0T2ZmZXJzEicuaG9zdC5pbnN0YWxsYXRpb24udjEuTGlzdE9mZmVyc1JlcXVlc3QaKC5ob3N0Lmluc3RhbGxhdGlvbi52MS5MaXN0T2ZmZXJzUmVzcG9uc2USVwoLUmV2aWV3T2ZmZXISIy5ob3N0Lmluc3RhbGxhdGlvbi52MS5SZXZpZXdSZXF1ZXN0GiMuaG9zdC5pbnN0YWxsYXRpb24udjEuUmV2aWV3ZWRPZmZlckI1WjNnYXRld2F5L3BrZy9nZW4vaG9zdC9pbnN0YWxsYXRpb24vdjE7aW5zdGFsbGF0aW9udjFiBnByb3RvMw"); + fileDesc("Cidob3N0L2luc3RhbGxhdGlvbi92MS9pbnN0YWxsYXRpb24ucHJvdG8SFGhvc3QuaW5zdGFsbGF0aW9uLnYxIkwKGkN1cnJlbnRJbnN0YWxsYXRpb25SZXF1ZXN0EhQKDHBhcmVudF90b2tlbhgBIAEoCRIYChBhY3Rvcl9jcmVkZW50aWFsGAIgASgJIm8KE0N1cnJlbnRJbnN0YWxsYXRpb24SCgoCaWQYASABKAkSFwoPb3JnYW5pemF0aW9uX2lkGAIgASgJEg4KBnRhcmdldBgDIAEoCRIQCghyZXZpc2lvbhgEIAEoBBIRCgluYW1lc3BhY2UYBSABKAkimQEKDkluc3RhbGxSZXF1ZXN0EhcKD29yZ2FuaXphdGlvbl9pZBgBIAEoCRIOCgZ0YXJnZXQYAiABKAkSDAoEcm9sZRgDIAEoCRIXCg9vd25lcl9wZXJzb25faWQYBCABKAkSNwoIYXBwcm92YWwYBSABKAsyJS5ob3N0Lmluc3RhbGxhdGlvbi52MS5Db25zZW50QXBwcm92YWwiOAoNUmV2aWV3UmVxdWVzdBIXCg9vcmdhbml6YXRpb25faWQYASABKAkSDgoGdGFyZ2V0GAIgASgJInkKD0NvbnNlbnRBcHByb3ZhbBIRCgl0YXJnZXRfaWQYASABKAkSEgoKZ2VuZXJhdGlvbhgCIAEoBBIZChFleHBlY3RlZF9yZXZpc2lvbhgDIAEoBBIUCgxvcGVyYXRpb25faWQYBCABKAkSDgoGZGlnZXN0GAUgASgJInYKEEdyYW50Um9sZVJlcXVlc3QSFwoPb3JnYW5pemF0aW9uX2lkGAEgASgJEhcKD2luc3RhbGxhdGlvbl9pZBgCIAEoCRIMCgRyb2xlGAMgASgJEhEKCXBlcnNvbl9pZBgEIAEoCRIPCgd0ZWFtX2lkGAUgASgJIncKEVJldm9rZVJvbGVSZXF1ZXN0EhcKD29yZ2FuaXphdGlvbl9pZBgBIAEoCRIXCg9pbnN0YWxsYXRpb25faWQYAiABKAkSDAoEcm9sZRgDIAEoCRIRCglwZXJzb25faWQYBCABKAkSDwoHdGVhbV9pZBgFIAEoCSJJChVMaXN0Um9sZUdyYW50c1JlcXVlc3QSFwoPb3JnYW5pemF0aW9uX2lkGAEgASgJEhcKD2luc3RhbGxhdGlvbl9pZBgCIAEoCSJJChZMaXN0Um9sZUdyYW50c1Jlc3BvbnNlEi8KBmdyYW50cxgBIAMoCzIfLmhvc3QuaW5zdGFsbGF0aW9uLnYxLlJvbGVHcmFudCI9CglSb2xlR3JhbnQSDAoEcm9sZRgBIAEoCRIRCglwZXJzb25faWQYAiABKAkSDwoHdGVhbV9pZBgDIAEoCSImCgtMaXN0UmVxdWVzdBIXCg9vcmdhbml6YXRpb25faWQYASABKAkiSQoMTGlzdFJlc3BvbnNlEjkKDWluc3RhbGxhdGlvbnMYASADKAsyIi5ob3N0Lmluc3RhbGxhdGlvbi52MS5JbnN0YWxsYXRpb24i3QIKDEluc3RhbGxhdGlvbhIKCgJpZBgBIAEoCRIXCg9vcmdhbml6YXRpb25faWQYAiABKAkSDgoGdGFyZ2V0GAMgASgJEjkKBnN0YXR1cxgEIAEoDjIpLmhvc3QuaW5zdGFsbGF0aW9uLnYxLkluc3RhbGxhdGlvbi5TdGF0dXMSEAoIcmV2aXNpb24YBSABKAQSDAoEcm9sZRgGIAEoCRIRCgluYW1lc3BhY2UYCSABKAkSFAoMcHJpbmNpcGFsX2lkGAogASgJEg0KBXJvbGVzGAsgAygJIlsKBlN0YXR1cxIWChJTVEFUVVNfVU5TUEVDSUZJRUQQABISCg5TVEFUVVNfUEVORElORxABEhEKDVNUQVRVU19BQ1RJVkUQAhISCg5TVEFUVVNfUkVWT0tFRBADSgQIBxAISgQICBAJUgh0ZWFtX2lkc1ISYWdlbnRfcHJpbmNpcGFsX2lkIk4KEkluc3RhbGxhdGlvbk5vdGljZRI4CgxpbnN0YWxsYXRpb24YASABKAsyIi5ob3N0Lmluc3RhbGxhdGlvbi52MS5JbnN0YWxsYXRpb24iEQoPSW5zdGFsbGF0aW9uQWNrIiwKEUxpc3RPZmZlcnNSZXF1ZXN0EhcKD29yZ2FuaXphdGlvbl9pZBgBIAEoCSJBChJMaXN0T2ZmZXJzUmVzcG9uc2USKwoGb2ZmZXJzGAEgAygLMhsuaG9zdC5pbnN0YWxsYXRpb24udjEuT2ZmZXIiKwoFT2ZmZXISDgoGdGFyZ2V0GAEgASgJEhIKCmdlbmVyYXRpb24YAiABKAQi7AIKDVJldmlld2VkT2ZmZXISNwoIYXBwcm92YWwYASABKAsyJS5ob3N0Lmluc3RhbGxhdGlvbi52MS5Db25zZW50QXBwcm92YWwSDgoGdGFyZ2V0GAIgASgJEi4KBXJvbGVzGAMgAygLMh8uaG9zdC5pbnN0YWxsYXRpb24udjEuT2ZmZXJSb2xlEjgKDGluc3RhbGxhdGlvbhgEIAEoCzIiLmhvc3QuaW5zdGFsbGF0aW9uLnYxLkluc3RhbGxhdGlvbhIzCgdjZWlsaW5nGAUgAygLMiIuaG9zdC5pbnN0YWxsYXRpb24udjEuT2ZmZXJCaW5kaW5nEhYKDnRvb2xzX2NvbXBvc2VkGAYgASgIEhkKEWluc3RhbGxhdGlvbl9yb2xlGAcgASgJEkAKEWVkZ2VfZGVwZW5kZW5jaWVzGAggAygLMiUuaG9zdC5pbnN0YWxsYXRpb24udjEuT2ZmZXJEZXBlbmRlbmN5IjMKD09mZmVyRGVwZW5kZW5jeRIOCgZtb2R1bGUYASABKAkSEAoIcHJlc2VuY2UYAiABKAkiYQoMT2ZmZXJCaW5kaW5nEg8KB2JpbmRpbmcYASABKAkSDgoGbW9kdWxlGAIgASgJEjAKBnNjb3BlcxgDIAMoCzIgLmhvc3QuaW5zdGFsbGF0aW9uLnYxLk9mZmVyU2NvcGUiiAEKCU9mZmVyUm9sZRIMCgRuYW1lGAEgASgJEjAKBnNjb3BlcxgCIAMoCzIgLmhvc3QuaW5zdGFsbGF0aW9uLnYxLk9mZmVyU2NvcGUSOwoMY2FwYWJpbGl0aWVzGAMgAygLMiUuaG9zdC5pbnN0YWxsYXRpb24udjEuT2ZmZXJDYXBhYmlsaXR5ImEKCk9mZmVyU2NvcGUSFQoNcmVzb3VyY2Vfa2luZBgBIAEoCRIPCgdhY3Rpb25zGAIgAygJEhUKDWF0dGFjaGVkX29ubHkYAyABKAgSFAoMcmVzb3VyY2VfaWRzGAQgAygJIpgBCg9PZmZlckNhcGFiaWxpdHkSDgoGbW9kdWxlGAEgASgJEhEKCW9wZXJhdGlvbhgCIAEoCRIyCghyZXF1aXJlcxgDIAMoCzIgLmhvc3QuaW5zdGFsbGF0aW9uLnYxLk9mZmVyU2NvcGUSLgoFdG9vbHMYBCADKAsyHy5ob3N0Lmluc3RhbGxhdGlvbi52MS5PZmZlclRvb2wiOQoJT2ZmZXJUb29sEgwKBG5hbWUYASABKAkSDwoHYmluZGluZxgCIAEoCRINCgV3cml0ZRgDIAEoCCIqCg9MaXN0SGVsZFJlcXVlc3QSFwoPb3JnYW5pemF0aW9uX2lkGAEgASgJIlEKEExpc3RIZWxkUmVzcG9uc2USPQoNaW5zdGFsbGF0aW9ucxgBIAMoCzImLmhvc3QuaW5zdGFsbGF0aW9uLnYxLkhlbGRJbnN0YWxsYXRpb24iWwoQSGVsZEluc3RhbGxhdGlvbhI4CgxpbnN0YWxsYXRpb24YASABKAsyIi5ob3N0Lmluc3RhbGxhdGlvbi52MS5JbnN0YWxsYXRpb24SDQoFcm9sZXMYAiADKAky8wUKE0luc3RhbGxhdGlvblNlcnZpY2USZgoHQ3VycmVudBIwLmhvc3QuaW5zdGFsbGF0aW9uLnYxLkN1cnJlbnRJbnN0YWxsYXRpb25SZXF1ZXN0GikuaG9zdC5pbnN0YWxsYXRpb24udjEuQ3VycmVudEluc3RhbGxhdGlvbhJTCgdJbnN0YWxsEiQuaG9zdC5pbnN0YWxsYXRpb24udjEuSW5zdGFsbFJlcXVlc3QaIi5ob3N0Lmluc3RhbGxhdGlvbi52MS5JbnN0YWxsYXRpb24SVAoGUmV2aWV3EiMuaG9zdC5pbnN0YWxsYXRpb24udjEuUmV2aWV3UmVxdWVzdBolLmhvc3QuaW5zdGFsbGF0aW9uLnYxLkNvbnNlbnRBcHByb3ZhbBJXCglHcmFudFJvbGUSJi5ob3N0Lmluc3RhbGxhdGlvbi52MS5HcmFudFJvbGVSZXF1ZXN0GiIuaG9zdC5pbnN0YWxsYXRpb24udjEuSW5zdGFsbGF0aW9uElkKClJldm9rZVJvbGUSJy5ob3N0Lmluc3RhbGxhdGlvbi52MS5SZXZva2VSb2xlUmVxdWVzdBoiLmhvc3QuaW5zdGFsbGF0aW9uLnYxLkluc3RhbGxhdGlvbhJrCg5MaXN0Um9sZUdyYW50cxIrLmhvc3QuaW5zdGFsbGF0aW9uLnYxLkxpc3RSb2xlR3JhbnRzUmVxdWVzdBosLmhvc3QuaW5zdGFsbGF0aW9uLnYxLkxpc3RSb2xlR3JhbnRzUmVzcG9uc2USTQoETGlzdBIhLmhvc3QuaW5zdGFsbGF0aW9uLnYxLkxpc3RSZXF1ZXN0GiIuaG9zdC5pbnN0YWxsYXRpb24udjEuTGlzdFJlc3BvbnNlElkKCExpc3RIZWxkEiUuaG9zdC5pbnN0YWxsYXRpb24udjEuTGlzdEhlbGRSZXF1ZXN0GiYuaG9zdC5pbnN0YWxsYXRpb24udjEuTGlzdEhlbGRSZXNwb25zZTJwChJJbnN0YWxsYWJsZVNlcnZpY2USWgoHSW5zdGFsbBIoLmhvc3QuaW5zdGFsbGF0aW9uLnYxLkluc3RhbGxhdGlvbk5vdGljZRolLmhvc3QuaW5zdGFsbGF0aW9uLnYxLkluc3RhbGxhdGlvbkFjazLIAQoMT2ZmZXJTZXJ2aWNlEl8KCkxpc3RPZmZlcnMSJy5ob3N0Lmluc3RhbGxhdGlvbi52MS5MaXN0T2ZmZXJzUmVxdWVzdBooLmhvc3QuaW5zdGFsbGF0aW9uLnYxLkxpc3RPZmZlcnNSZXNwb25zZRJXCgtSZXZpZXdPZmZlchIjLmhvc3QuaW5zdGFsbGF0aW9uLnYxLlJldmlld1JlcXVlc3QaIy5ob3N0Lmluc3RhbGxhdGlvbi52MS5SZXZpZXdlZE9mZmVyQjVaM2dhdGV3YXkvcGtnL2dlbi9ob3N0L2luc3RhbGxhdGlvbi92MTtpbnN0YWxsYXRpb252MWIGcHJvdG8z"); /** * @generated from message host.installation.v1.CurrentInstallationRequest @@ -697,6 +697,22 @@ export type ReviewedOffer = Message<"host.installation.v1.ReviewedOffer"> & { * @generated from field: bool tools_composed = 6; */ toolsComposed: boolean; + + /** + * Signed machine-role selector: empty is undeclared, "none" is no standing + * authority, otherwise a named role. This is not a person or team grant. + * + * @generated from field: string installation_role = 7; + */ + installationRole: string; + + /** + * Exact declared viewer-edge presence, covered by the reviewed digest. + * Required access and per-module approval are separate host review concerns. + * + * @generated from field: repeated host.installation.v1.OfferDependency edge_dependencies = 8; + */ + edgeDependencies: OfferDependency[]; }; /** @@ -706,6 +722,31 @@ export type ReviewedOffer = Message<"host.installation.v1.ReviewedOffer"> & { export const ReviewedOfferSchema: GenMessage = /*@__PURE__*/ messageDesc(file_host_installation_v1_installation, 18); +/** + * OfferDependency carries presence only, never scopes or inferred binding ids. + * Module is the exact signed principal, resolved by the host to its live target. + * + * @generated from message host.installation.v1.OfferDependency + */ +export type OfferDependency = Message<"host.installation.v1.OfferDependency"> & { + /** + * @generated from field: string module = 1; + */ + module: string; + + /** + * @generated from field: string presence = 2; + */ + presence: string; +}; + +/** + * Describes the message host.installation.v1.OfferDependency. + * Use `create(OfferDependencySchema)` to create a new message. + */ +export const OfferDependencySchema: GenMessage = /*@__PURE__*/ + messageDesc(file_host_installation_v1_installation, 19); + /** * OfferBinding is one declared call from the solution to a module. * @@ -735,7 +776,7 @@ export type OfferBinding = Message<"host.installation.v1.OfferBinding"> & { * Use `create(OfferBindingSchema)` to create a new message. */ export const OfferBindingSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_host_installation_v1_installation, 19); + messageDesc(file_host_installation_v1_installation, 20); /** * OfferRole is one declared role and what a grant of it reaches. @@ -768,7 +809,7 @@ export type OfferRole = Message<"host.installation.v1.OfferRole"> & { * Use `create(OfferRoleSchema)` to create a new message. */ export const OfferRoleSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_host_installation_v1_installation, 20); + messageDesc(file_host_installation_v1_installation, 21); /** * OfferScope is one declared scope. @@ -806,7 +847,7 @@ export type OfferScope = Message<"host.installation.v1.OfferScope"> & { * Use `create(OfferScopeSchema)` to create a new message. */ export const OfferScopeSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_host_installation_v1_installation, 21); + messageDesc(file_host_installation_v1_installation, 22); /** * OfferCapability is one module operation a role's scopes cover. @@ -847,7 +888,7 @@ export type OfferCapability = Message<"host.installation.v1.OfferCapability"> & * Use `create(OfferCapabilitySchema)` to create a new message. */ export const OfferCapabilitySchema: GenMessage = /*@__PURE__*/ - messageDesc(file_host_installation_v1_installation, 22); + messageDesc(file_host_installation_v1_installation, 23); /** * OfferTool is one tool-exposed Runnable of an operation. @@ -882,7 +923,7 @@ export type OfferTool = Message<"host.installation.v1.OfferTool"> & { * Use `create(OfferToolSchema)` to create a new message. */ export const OfferToolSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_host_installation_v1_installation, 23); + messageDesc(file_host_installation_v1_installation, 24); /** * ListHeldRequest names the organisation the person reads at: the one their @@ -904,7 +945,7 @@ export type ListHeldRequest = Message<"host.installation.v1.ListHeldRequest"> & * Use `create(ListHeldRequestSchema)` to create a new message. */ export const ListHeldRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_host_installation_v1_installation, 24); + messageDesc(file_host_installation_v1_installation, 25); /** * ListHeldResponse is every installation the person holds a role on, sorted @@ -926,7 +967,7 @@ export type ListHeldResponse = Message<"host.installation.v1.ListHeldResponse"> * Use `create(ListHeldResponseSchema)` to create a new message. */ export const ListHeldResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_host_installation_v1_installation, 25); + messageDesc(file_host_installation_v1_installation, 26); /** * HeldInstallation is one installation the person may use. @@ -954,7 +995,7 @@ export type HeldInstallation = Message<"host.installation.v1.HeldInstallation"> * Use `create(HeldInstallationSchema)` to create a new message. */ export const HeldInstallationSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_host_installation_v1_installation, 26); + messageDesc(file_host_installation_v1_installation, 27); /** * InstallationService is implemented by the host and by the local authority. diff --git a/libraries/authority-core/go/authority/check.go b/libraries/authority-core/go/authority/check.go index 69b72ff3..6d594547 100644 --- a/libraries/authority-core/go/authority/check.go +++ b/libraries/authority-core/go/authority/check.go @@ -58,10 +58,23 @@ func (s *Service) Check(ctx context.Context, token, audience string) (*workconte // method is Check alone. Advisory decides the method but does not enforce it: // a sound capability is answered with the method's refusal beside it, the // same refusal a check that enforces it returns as its error. +// The exact reserved InstallationStepHTTPMethod (POST /_codefly/installations) +// and InstallationStepRPCMethod (/host.installation.v1.InstallableService/Install) +// instead require a setup-only credential for the current consented pending +// attempt, independently of delivered method policy. Ordinary capabilities are +// refused there. The receiver supplies its actual method and delivered audience; +// it compares verified organization and installation seal with body and carriers +// before callback or cached acknowledgement. Advisory never relaxes this path. func (s *Service) CheckMethod(ctx context.Context, token, audience, method string, advisory bool) (verified *workcontext.Verified, revision uint64, refusal error, err error) { if audience == "" { return nil, 0, nil, domain.Refuse("a check names the audience the caller is") } + if method == InstallationStepHTTPMethod || method == InstallationStepRPCMethod { + // Setup credentials never enter ordinary checking. Advisory cannot + // relax this authentication boundary or activate an installation. + verified, revision, err = s.VerifyInstallationStep(ctx, token, audience) + return verified, revision, nil, err + } if verified, revision, err = s.Check(ctx, token, audience); err != nil || method == "" { return verified, revision, nil, err } diff --git a/libraries/authority-core/go/authority/harness_test.go b/libraries/authority-core/go/authority/harness_test.go index f40ff8d8..6a6e478c 100644 --- a/libraries/authority-core/go/authority/harness_test.go +++ b/libraries/authority-core/go/authority/harness_test.go @@ -84,6 +84,8 @@ func newHarness(t *testing.T) *harness { for _, org := range []string{acme, globex} { h.store.PutOrganization(domain.Organization{ID: org, Name: org}) } + h.store.PutTeam(acme, "team") + h.store.PutTeam(globex, "ops") h.store.PutOrgRole(domain.OrgRole{Organization: acme, Name: "admin", Scopes: nil}) h.store.PutOrgRole(domain.OrgRole{Organization: globex, Name: "admin", Scopes: nil}) h.store.PutOrgRole(domain.OrgRole{Organization: acme, Name: "store-admin", Scopes: []domain.RoleScope{{Kind: kind, Actions: []string{"admin"}, Resources: []resources.Grant{{ID: "store-x"}}}}}) diff --git a/libraries/authority-core/go/authority/installation_declaration_test.go b/libraries/authority-core/go/authority/installation_declaration_test.go new file mode 100644 index 00000000..5ae9d0a3 --- /dev/null +++ b/libraries/authority-core/go/authority/installation_declaration_test.go @@ -0,0 +1,235 @@ +package authority_test + +import ( + "context" + "crypto/sha256" + "encoding/json" + "errors" + "fmt" + "strings" + "testing" + + "github.com/codefly-dev/authority/libraries/authority-core/go/authority" + "github.com/codefly-dev/authority/libraries/authority-core/go/domain" +) + +func declaration(t *testing.T, h *harness, selector string) domain.Module { + t.Helper() + m := solution("b", true) + m.Generation++ + m.InstallationRole = selector + if _, err := h.svc.ApplyModule(h.ctx, m); err != nil { + t.Fatal(err) + } + return m +} + +func TestInstallationDeclarationSelection(t *testing.T) { + for _, tc := range []struct { + name, selector, requested, want string + refused bool + }{ + {"legacy", "", "author", "author", false}, + {"legacy missing", "", "", "", true}, + {"legacy caller none", "", "none", "", true}, + {"named default", "editor", "", "editor", false}, + {"named same", "editor", "editor", "editor", false}, + {"named override", "editor", "author", "", true}, + {"named missing", "missing", "", "", true}, + {"declared none", "none", "", "none", false}, + {"none caller none", "none", "none", "", true}, + {"none override", "none", "author", "", true}, + } { + t.Run(tc.name, func(t *testing.T) { + h := newHarness(t) + declaration(t, h, tc.selector) + admin := h.admin(globex) + calls := 0 + inst, err := h.svc.Install(h.ctx, admin, globex, "b", tc.requested, admin.ID, h.approval(globex, "b"), func(context.Context, domain.Installation) error { calls++; return nil }) + if tc.refused { + if err == nil || calls != 0 { + t.Fatalf("accepted refused selection: %+v %v, setup calls %d", inst, err, calls) + } + return + } + if err != nil || inst.Status != domain.Active || inst.AgentRole != tc.want || calls != 1 { + t.Fatalf("selection: %+v %v", inst, err) + } + }) + } +} + +func TestNoStandingAuthorityStillHasFencedSetup(t *testing.T) { + h := newHarness(t) + m := declaration(t, h, "none") + if _, exists := m.Roles["none"]; exists { + t.Fatal("fixture contains a synthetic role") + } + admin := h.admin(globex) + approval := h.approval(globex, "b") + inst, err := h.svc.Install(h.ctx, admin, globex, "b", "", admin.ID, approval, func(context.Context, domain.Installation) error { return awaitingStep }) + if !errors.Is(err, awaitingStep) || inst.AgentRole != "none" || inst.Status != domain.Pending { + t.Fatalf("pending: %+v %v", inst, err) + } + attempt := authority.InstallationStepAttempt{InstallationID: inst.ID, Organization: globex, Target: "b", TargetID: approval.TargetID, Revision: inst.Revision, Generation: approval.Generation, Consent: inst.Consent, Digest: approval.Digest} + issued := issueStep(t, h, attempt) + if _, _, err = h.svc.VerifyInstallationStep(h.ctx, issued.Token, approval.TargetID); err != nil { + t.Fatal(err) + } + active, err := h.svc.Install(h.ctx, admin, globex, "b", "", admin.ID, approval, func(context.Context, domain.Installation) error { return nil }) + if err != nil || active.Status != domain.Active || active.PrincipalID != inst.PrincipalID || active.Consent != inst.Consent { + t.Fatalf("resume: %+v %v", active, err) + } + if _, _, err = h.svc.VerifyInstallationStep(h.ctx, issued.Token, approval.TargetID); err == nil { + t.Fatal("setup survived activation") + } + for _, req := range []authority.IssueRequest{{Audience: "b"}, {Audience: "b", Scopes: m.Roles["author"]}} { + if out, err := h.svc.IssueForInstallation(h.ctx, authority.Workload{Module: "b"}, inst.ID, req); err == nil || out.Token != "" { + t.Fatal("zero standing authority issued", err) + } + } + if err := h.store.Read(h.ctx, func(tx authority.Tx) error { + grants, err := tx.RoleGrants(inst.ID) + if len(grants) != 0 { + t.Fatal("automatic grants", grants) + } + current, err2 := tx.ModuleNamed("b") + if _, exists := current.Roles["none"]; exists { + t.Fatal("synthetic role created") + } + if err != nil { + return err + } + return err2 + }); err != nil { + t.Fatal(err) + } + // People continue to need a separate grant, including the installer. + if out, err := h.svc.IssueForPerson(h.ctx, admin, authority.IssueRequest{Audience: "b"}); err == nil || out.Token != "" { + t.Fatal("installer gained solution authority", err) + } +} + +func TestInstallationDeclarationDoesNotRelaxRoles(t *testing.T) { + for _, fault := range []string{"empty role", "missing installer"} { + t.Run(fault, func(t *testing.T) { + h := newHarness(t) + m := solution("b", true) + m.Generation++ + m.InstallationRole = "none" + if fault == "empty role" { + m.Roles["editor"] = nil + } else { + delete(m.Roles, domain.InstallerRole) + } + if _, err := h.svc.ApplyModule(h.ctx, m); err == nil { + t.Fatal("role rule relaxed") + } + }) + } +} + +func TestInstallationDeclarationDigestAndStaleApproval(t *testing.T) { + for _, field := range []string{"selector", "edges"} { + t.Run(field, func(t *testing.T) { + h := newHarness(t) + approval := h.approval(globex, "b") + err := h.store.Write(h.ctx, func(tx authority.Tx) error { + m, err := tx.ModuleNamed("b") + if err != nil { + return err + } + if field == "selector" { + m.InstallationRole = "author" + } else { + m.EdgeDependencies = []domain.EdgeDependency{{Module: "catalogue-id", Presence: "required"}} + } + return tx.PutModule(m) + }) + if err != nil { + t.Fatal(err) + } + admin := h.admin(globex) + _, err = h.svc.Install(h.ctx, admin, globex, "b", "author", admin.ID, approval, nil) + if err == nil || !strings.Contains(err.Error(), "DIGEST_CHANGED") { + t.Fatal("changed declaration reused review", err) + } + }) + } +} + +func TestInstallationDeclarationLegacyDigestBytes(t *testing.T) { + // Captured field order/representation from Module at parent 940ddd642b55. + const legacy = `{"Name":"legacy","Principal":"principal","Kind":"solution","Generation":1,"StandIn":false,"ResourceKinds":null,"Bindings":null,"Roles":null,"OwnedRoles":null,"OwnerAware":false,"Methods":null}` + m := domain.Module{Name: "legacy", Principal: "principal", Kind: domain.KindSolution, Generation: 1} + for _, edges := range [][]domain.EdgeDependency{nil, {}} { + m.EdgeDependencies = edges + raw, err := json.Marshal(m) + if err != nil { + t.Fatal(err) + } + if string(raw) != legacy { + t.Fatalf("legacy JSON changed: %s", raw) + } + } + h := newHarness(t) + var mStored domain.Module + if err := h.store.Read(h.ctx, func(tx authority.Tx) error { var err error; mStored, err = tx.ModuleNamed("b"); return err }); err != nil { + t.Fatal(err) + } + raw, err := json.Marshal(mStored) + if err != nil { + t.Fatal(err) + } + want := fmt.Sprintf("sha256:%x", sha256.Sum256(raw)) + if got := h.approval(globex, "b").Digest; got != want { + t.Fatalf("digest %s != %s", got, want) + } +} + +func TestNamedInstallationDeclarationRetryPreservesOriginalRequest(t *testing.T) { + h := newHarness(t) + declaration(t, h, "editor") + admin := h.admin(globex) + approval := h.approval(globex, "b") + step := func(context.Context, domain.Installation) error { return awaitingStep } + first, err := h.svc.Install(h.ctx, admin, globex, "b", "", admin.ID, approval, step) + if !errors.Is(err, awaitingStep) { + t.Fatal(err) + } + // Equal effective authority does not make two different requests the same + // reviewed operation. Retain the original empty request on retries. + _, err = h.svc.Install(h.ctx, admin, globex, "b", "editor", admin.ID, approval, step) + refused(t, err, "INSTALLATION_APPROVAL_OPERATION_CONFLICT") + again, err := h.svc.Install(h.ctx, admin, globex, "b", "", admin.ID, approval, step) + if !errors.Is(err, awaitingStep) || again.Consent != first.Consent || again.AgentRole != "editor" { + t.Fatalf("retry: %+v %v", again, err) + } +} + +func TestInstallationDeclarationStoreSnapshotsDoNotAlias(t *testing.T) { + h := newHarness(t) + m := solution("b", true) + m.Generation++ + m.InstallationRole = "none" + m.EdgeDependencies = []domain.EdgeDependency{{Module: "catalogue-id", Presence: "required"}} + if _, err := h.svc.ApplyModule(h.ctx, m); err != nil { + t.Fatal(err) + } + m.EdgeDependencies[0].Module = "mutated caller" + offer, err := h.svc.ReviewOffer(h.ctx, h.admin(globex), globex, "b") + if err != nil { + t.Fatal(err) + } + if offer.Module.EdgeDependencies[0].Module != "catalogue-id" { + t.Fatal("store aliases input") + } + offer.Module.EdgeDependencies[0].Module = "mutated review" + again, err := h.svc.ReviewOffer(h.ctx, h.admin(globex), globex, "b") + if err != nil { + t.Fatal(err) + } + if again.Module.EdgeDependencies[0].Module != "catalogue-id" || again.Approval.Digest != offer.Approval.Digest { + t.Fatal("store aliases output") + } +} diff --git a/libraries/authority-core/go/authority/installation_step.go b/libraries/authority-core/go/authority/installation_step.go new file mode 100644 index 00000000..a751d1c5 --- /dev/null +++ b/libraries/authority-core/go/authority/installation_step.go @@ -0,0 +1,205 @@ +package authority + +import ( + "context" + "crypto/sha256" + "encoding/json" + "fmt" + "time" + + "github.com/codefly-dev/authority/libraries/authority-core/go/domain" + basev0 "github.com/codefly-dev/core/generated/go/codefly/base/v0" + "github.com/codefly-dev/core/workcontext" + "google.golang.org/protobuf/proto" +) + +// InstallationStepHTTPMethod and InstallationStepRPCMethod are setup-only +// admission points. A receiver supplies the method it serves, never a body field. +const ( + InstallationStepHTTPMethod = "POST /_codefly/installations" + InstallationStepRPCMethod = "/host.installation.v1.InstallableService/Install" + MaxInstallationStepTTL = time.Minute +) + +// InstallationStepAttempt is the canonical pending record and original reviewed +// delivery identity retained by the host's durable job. Target is its composed +// name; TargetID is the delivered principal (not a host release-table UUID). +// This is not a caller's authority request: only the trusted host job claimant +// may ask for issuance, after checking its lease and immutable release binding. +type InstallationStepAttempt struct { + InstallationID, Organization, Target, TargetID string + Revision, Generation uint64 + Consent domain.ConsentReceipt + Digest string +} + +// IssueInstallationStep grants only delivery of this installation's setup, not +// its agent role or any data access. ttl must fit the claimant's remaining lease. +// Queue custody stays with the host; lease loss is not an Authority state change. +// A successful enqueue is not an acknowledgement: InstallStep must still return +// an error until this exact job succeeds, and Install alone activates the record. +func (s *Service) IssueInstallationStep(ctx context.Context, attempt InstallationStepAttempt, ttl time.Duration) (Issued, error) { + if ttl <= 0 { + return Issued{}, domain.Refuse("installation step requires a positive remaining lease") + } + now := s.cfg.Clock() + ttl = min(ttl, MaxInstallationStepTTL, s.cfg.MaxTTL) + src, err := s.stepSource(ctx, attempt.InstallationID) + if err != nil { + return Issued{}, err + } + if src.attempt != attempt { + return Issued{}, domain.NotNow("installation step attempt changed") + } + minter := *s.authority + minter.Seals = src + minter.Now = func() time.Time { return now } + token, claims, err := minter.Start(ctx, workcontext.StartInput{ + TenantID: attempt.Organization, OrganizationID: attempt.Organization, + OwnerPrincipalID: src.principal, OwnerPrincipalKind: "service", + TaskID: attempt.Consent.OperationID, Audience: attempt.TargetID, + InstallationID: attempt.InstallationID, OperationBindingID: src.bindingID(), + AuthorityScopes: Scopes{stepScope(attempt.InstallationID)}, TTL: ttl, + ReplayPolicy: workcontext.ReplayIdempotent, + }) + if err != nil { + return Issued{}, err + } + if err := src.current(ctx); err != nil { + return Issued{}, err + } + if claims.GetAuthorizationRevision() != src.revision || !s.cfg.Clock().Before(time.Unix(claims.GetExpiresAtUnix(), 0)) { + return Issued{}, domain.NotNow("installation step changed or expired during issuance") + } + return Issued{token, claims}, nil +} + +// VerifyInstallationStep is only for an authenticated setup receiver. CheckMethod +// exposes it through the existing Check transport at the two exact setup methods. +// Ordinary Verify, Check, issuance and child exchange never use its seal source. +// The receiver must compare the verified organization, installation and revision +// with the strict request body/carriers before its callback OR cached acknowledgement. +func (s *Service) VerifyInstallationStep(ctx context.Context, token, audience string) (*workcontext.Verified, uint64, error) { + v, err := s.verifier(ctx, audience) + if err != nil { + return nil, 0, err + } + inspector := workcontext.Inspector{Issuer: v.Issuer, Keys: v.Keys, Now: v.Now} + if _, err := inspector.Inspect(token, audience); err != nil { + return nil, 0, err + } + decoded, err := workcontext.Decode(token) + if err != nil { + return nil, 0, err + } + // Inspection authenticates the lookup key but grants nothing. Core's full + // verifier still checks every live source before anything is returned. + wc := decoded.Context() + src, err := s.stepSource(ctx, wc.GetSeal().GetInstallationId()) + if err != nil { + return nil, 0, err + } + a := src.attempt + if audience == "" || audience != a.TargetID || wc.GetTenantId() != a.Organization || wc.GetOrganizationId() != a.Organization || + wc.GetOwnerPrincipalId() != src.principal || wc.GetOwnerPrincipalKind() != "service" || wc.GetTaskId() != a.Consent.OperationID || + len(wc.GetActorChain()) != 0 || len(wc.GetAuthorityScopes()) != 1 || !proto.Equal(wc.GetAuthorityScopes()[0], stepScope(a.InstallationID)) || + wc.GetOperationBinding().GetBindingId() != src.bindingID() || wc.GetReplayPolicy() != workcontext.ReplayIdempotent { + return nil, 0, domain.Refuse("not this installation's setup credential") + } + v.Seals = src + verified, err := v.Verify(ctx, token) + if err != nil { + return nil, 0, err + } + if err := src.current(ctx); err != nil { + return nil, 0, err + } + // The owner is checking its own lease-bounded credential. Core's default + // network clock tolerance must not extend this setup delivery window. + if now := s.cfg.Clock(); now.Before(time.Unix(wc.GetNotBeforeUnix(), 0)) || !now.Before(time.Unix(wc.GetExpiresAtUnix(), 0)) { + return nil, 0, domain.NotNow("installation step credential is outside its delivery window") + } + return verified, src.revision, nil +} + +func stepScope(id string) *basev0.WorkScopeV1 { + return &basev0.WorkScopeV1{ResourceKind: "installations", Actions: []string{"install"}, ResourceIds: []string{id}} +} + +// stepSeals is private and answers only this pending attempt. Normal seals keep +// their active-only contract. Its opaque binding cannot resolve as a normal +// organization/module/binding, including after activation at the same revision. +type stepSeals struct { + s *Service + attempt InstallationStepAttempt + principal string + revision uint64 +} + +func (s *Service) stepSource(ctx context.Context, id string) (stepSeals, error) { + out := stepSeals{s: s} + err := s.store.Read(ctx, func(tx Tx) error { + inst, err := tx.Installation(id) + if err != nil { + return notFound(err) + } + if inst.Status != domain.Pending || inst.ID == "" || inst.Organization == "" || inst.PrincipalID == "" || inst.Revision == 0 || + inst.Consent.OperationID == "" || inst.Consent.Intent == "" || inst.Consent.Revision != inst.Revision { + return domain.NotNow("installation has no current consented pending attempt") + } + m, err := tx.ModuleNamed(inst.Target) + if err != nil { + return notFound(err) + } + if m.Kind != domain.KindSolution || m.Principal == "" || m.Generation == 0 { + return domain.Refuse("setup requires a delivered solution") + } + digest, err := offerDigest(m) + if err != nil { + return err + } + out.attempt = InstallationStepAttempt{inst.ID, inst.Organization, inst.Target, m.Principal, inst.Revision, m.Generation, inst.Consent, digest} + out.principal = inst.PrincipalID + out.revision, err = tx.AuthorizationRevision(inst.Organization) + return err + }) + return out, err +} + +func (src stepSeals) bindingID() string { + raw, _ := json.Marshal(src.attempt) // this value contains only strings and integers + return fmt.Sprintf("installation-step:%x", sha256.Sum256(raw)) +} + +func (src stepSeals) current(ctx context.Context) error { + now, err := src.s.stepSource(ctx, src.attempt.InstallationID) + if err != nil { + return err + } + if now.attempt != src.attempt || now.principal != src.principal || now.revision != src.revision { + return domain.NotNow("installation step attempt changed") + } + return nil +} + +func (src stepSeals) Seal(ctx context.Context, principal, id string) (workcontext.Seal, error) { + if principal != src.principal || id != src.attempt.InstallationID { + return workcontext.Seal{}, workcontext.ErrNoSeal + } + return workcontext.Seal{InstallationID: id, InstallationRevision: src.attempt.Revision}, src.current(ctx) +} + +func (src stepSeals) PrincipalEpoch(ctx context.Context, principal string) (uint64, error) { + if principal != src.principal { + return 0, workcontext.ErrNoSeal + } + return src.attempt.Revision, src.current(ctx) +} + +func (src stepSeals) OperationBinding(ctx context.Context, id string) (workcontext.OperationBinding, error) { + if id != src.bindingID() { + return workcontext.OperationBinding{}, workcontext.ErrNoBinding + } + return workcontext.OperationBinding{ID: id, PrincipalID: src.principal, InstallationID: src.attempt.InstallationID, + Revision: src.attempt.Revision, Incarnation: src.attempt.Generation}, src.current(ctx) +} diff --git a/libraries/authority-core/go/authority/installation_step_test.go b/libraries/authority-core/go/authority/installation_step_test.go new file mode 100644 index 00000000..18aa9678 --- /dev/null +++ b/libraries/authority-core/go/authority/installation_step_test.go @@ -0,0 +1,287 @@ +package authority_test + +import ( + "context" + "errors" + "fmt" + "testing" + "time" + + "github.com/codefly-dev/authority/libraries/authority-core/go/authority" + "github.com/codefly-dev/authority/libraries/authority-core/go/domain" +) + +var awaitingStep = errors.New("setup not acknowledged") + +func pendingStep(t *testing.T) (*harness, authority.InstallationStepAttempt, *domain.ConsentApproval) { + t.Helper() + h := newHarness(t) + approval := h.approval(globex, "b") + admin := h.admin(globex) + inst, err := h.svc.Install(h.ctx, admin, globex, "b", "author", admin.ID, approval, func(context.Context, domain.Installation) error { return awaitingStep }) + if !errors.Is(err, awaitingStep) || inst.Status != domain.Pending { + t.Fatalf("pending: %+v %v", inst, err) + } + return h, authority.InstallationStepAttempt{InstallationID: inst.ID, Organization: inst.Organization, Target: inst.Target, TargetID: approval.TargetID, Revision: inst.Revision, Generation: approval.Generation, Consent: inst.Consent, Digest: approval.Digest}, approval +} +func issueStep(t *testing.T, h *harness, a authority.InstallationStepAttempt) authority.Issued { + t.Helper() + issued, err := h.svc.IssueInstallationStep(h.ctx, a, 30*time.Second) + if err != nil { + t.Fatal(err) + } + return issued +} +func TestInstallationStepOnlyAdmitsExactSetupMethods(t *testing.T) { + h, a, approval := pendingStep(t) + issued := issueStep(t, h, a) + wc := issued.Context + if wc.GetSeal().GetInstallationRevision() != a.Revision || wc.GetOrganizationId() != a.Organization || len(wc.GetAuthorityScopes()) != 1 || wc.GetAuthorityScopes()[0].GetResourceKind() != "installations" { + t.Fatalf("wrong setup claims: %v", wc) + } + for _, method := range []string{authority.InstallationStepHTTPMethod, authority.InstallationStepRPCMethod} { + for _, advisory := range []bool{false, true} { + v, _, refusal, err := h.svc.CheckMethod(h.ctx, issued.Token, a.TargetID, method, advisory) + if err != nil || refusal != nil || v == nil { + t.Fatalf("%s: %v %v", method, refusal, err) + } + } + } + for _, method := range []string{"", "GET /_codefly/installations", "POST /_codefly/installations/", "POST /other"} { + if _, _, _, err := h.svc.CheckMethod(h.ctx, issued.Token, a.TargetID, method, true); err == nil { + t.Fatalf("admitted %q", method) + } + } + if _, err := h.svc.Verify(h.ctx, issued.Token, a.TargetID); err == nil { + t.Fatal("ordinary verify accepted") + } + if _, err := h.svc.IssueForInstallation(h.ctx, authority.Workload{Module: a.Target}, a.InstallationID, authority.IssueRequest{Audience: a.TargetID}); err == nil { + t.Fatal("ordinary pending issue accepted") + } + if _, _, err := h.svc.VerifyInstallationStep(h.ctx, issued.Token, "a"); err == nil { + t.Fatal("wrong receiver accepted") + } + if _, _, err := h.svc.VerifyInstallationStep(h.ctx, issued.Token+"x", a.TargetID); err == nil { + t.Fatal("tampered token accepted") + } + admin := h.admin(globex) + inst, err := h.svc.Install(h.ctx, admin, globex, "b", "author", admin.ID, approval, func(context.Context, domain.Installation) error { return nil }) + if err != nil || inst.Status != domain.Active { + t.Fatalf("ack: %+v %v", inst, err) + } + if _, _, err := h.svc.VerifyInstallationStep(h.ctx, issued.Token, a.TargetID); err == nil { + t.Fatal("active setup token accepted") + } + if _, err := h.svc.Verify(h.ctx, issued.Token, a.TargetID); err == nil { + t.Fatal("activation converted setup token into ordinary authority") + } + err = h.store.Read(h.ctx, func(tx authority.Tx) error { + grants, e := tx.RoleGrants(a.InstallationID) + if len(grants) != 0 { + t.Fatal("automatic grants", grants) + } + return e + }) + if err != nil { + t.Fatal(err) + } +} +func TestInstallationStepRejectsEveryChangedAttemptField(t *testing.T) { + cases := map[string]func(*authority.InstallationStepAttempt){ + "id": func(a *authority.InstallationStepAttempt) { a.InstallationID = "missing" }, + "organization": func(a *authority.InstallationStepAttempt) { a.Organization = acme }, + "target": func(a *authority.InstallationStepAttempt) { a.Target = "a" }, + "principal": func(a *authority.InstallationStepAttempt) { a.TargetID = "a" }, + "revision": func(a *authority.InstallationStepAttempt) { a.Revision++ }, + "generation": func(a *authority.InstallationStepAttempt) { a.Generation++ }, + "digest": func(a *authority.InstallationStepAttempt) { a.Digest = "other" }, + "operation": func(a *authority.InstallationStepAttempt) { a.Consent.OperationID = "other" }, + "intent": func(a *authority.InstallationStepAttempt) { a.Consent.Intent = "other" }, + "receipt revision": func(a *authority.InstallationStepAttempt) { a.Consent.Revision++ }, + } + for name, change := range cases { + t.Run(name, func(t *testing.T) { + h, a, _ := pendingStep(t) + change(&a) + if got, err := h.svc.IssueInstallationStep(h.ctx, a, time.Minute); err == nil || got.Token != "" { + t.Fatal("changed attempt issued", err) + } + }) + } +} +func TestInstallationStepRevocationAndReplacement(t *testing.T) { + for _, change := range []string{"uninstall", "approval", "delivery", "authorization", "receipt", "activation"} { + t.Run(change, func(t *testing.T) { + h, a, approval := pendingStep(t) + issued := issueStep(t, h, a) + admin := h.admin(globex) + var err error + switch change { + case "uninstall": + err = h.svc.Uninstall(h.ctx, admin, globex, a.InstallationID) + case "approval": + _, err = h.svc.Install(h.ctx, admin, globex, "b", "author", admin.ID, h.approval(globex, "b"), func(context.Context, domain.Installation) error { return awaitingStep }) + if errors.Is(err, awaitingStep) { + err = nil + } + case "delivery": + m := solution("b", false) + m.Generation++ + _, err = h.svc.ApplyModule(h.ctx, m) + case "authorization": + err = h.store.Write(h.ctx, func(tx authority.Tx) error { return tx.BumpAuthorizationRevision(globex) }) + case "receipt": + err = h.store.Write(h.ctx, func(tx authority.Tx) error { + i, e := tx.Installation(a.InstallationID) + if e != nil { + return e + } + i.Consent = domain.ConsentReceipt{} + return tx.PutInstallation(i) + }) + case "activation": + _, err = h.svc.Install(h.ctx, admin, globex, "b", "author", admin.ID, approval, func(context.Context, domain.Installation) error { return nil }) + } + if err != nil { + t.Fatal(err) + } + if _, _, err = h.svc.VerifyInstallationStep(h.ctx, issued.Token, a.TargetID); err == nil { + t.Fatal("stale step verified") + } + if change != "authorization" { + if _, err = h.svc.IssueInstallationStep(h.ctx, a, time.Minute); err == nil { + t.Fatal("stale attempt issued") + } + } + }) + } +} +func TestInstallationStepLeaseBoundAndExactExpiry(t *testing.T) { + h, a, _ := pendingStep(t) + for _, ttl := range []time.Duration{0, -time.Second} { + if _, err := h.svc.IssueInstallationStep(h.ctx, a, ttl); err == nil { + t.Fatal("nonpositive lease accepted") + } + } + issued, err := h.svc.IssueInstallationStep(h.ctx, a, 10*time.Minute) + if err != nil { + t.Fatal(err) + } + if issued.Context.GetExpiresAtUnix()-h.now.Unix() != 60 { + t.Fatal("TTL not capped") + } + h.now = h.now.Add(60 * time.Second) + if _, _, err := h.svc.VerifyInstallationStep(h.ctx, issued.Token, a.TargetID); err == nil { + t.Fatal("Core clock skew extended setup lease") + } + fresh := issueStep(t, h, a) + if _, _, err := h.svc.VerifyInstallationStep(h.ctx, fresh.Token, a.TargetID); err != nil { + t.Fatal("pending retry failed", err) + } +} + +// Deterministic state changes after a read exercise the decision/mint/verify +// window. The final successful read is admission, not a reservation through I/O. +type stepRaceStore struct { + authority.Store + at, reads int + change func() +} + +func (s *stepRaceStore) Read(ctx context.Context, fn func(authority.Tx) error) error { + err := s.Store.Read(ctx, fn) + s.reads++ + if s.reads == s.at { + s.change() + } + return err +} +func TestInstallationStepFencesEveryReadBoundary(t *testing.T) { + for _, verify := range []bool{false, true} { + for at := 1; at <= 8; at++ { + t.Run(fmt.Sprintf("verify=%v/read=%d", verify, at), func(t *testing.T) { + h, a, _ := pendingStep(t) + issued := issueStep(t, h, a) + changed := false + store := &stepRaceStore{Store: h.store, at: at, change: func() { + changed = true + if err := h.svc.Uninstall(h.ctx, h.admin(globex), globex, a.InstallationID); err != nil { + t.Fatal(err) + } + }} + svc, err := authority.New(store, h.cfg) + if err != nil { + t.Fatal(err) + } + if verify { + _, _, err = svc.VerifyInstallationStep(h.ctx, issued.Token, a.TargetID) + } else { + _, err = svc.IssueInstallationStep(h.ctx, a, time.Minute) + } + if changed && store.reads > at && err == nil { + t.Fatal("state changed before final admission") + } + }) + } + } +} +func TestInstallationStepNormalActiveTokenCannotSetup(t *testing.T) { + h := newHarness(t) + inst := h.install(globex, "b") + token, err := h.svc.IssueForInstallation(h.ctx, authority.Workload{Module: "b"}, inst.ID, authority.IssueRequest{Audience: "b"}) + if err != nil { + t.Fatal(err) + } + if _, _, _, err := h.svc.CheckMethod(h.ctx, token.Token, "b", authority.InstallationStepHTTPMethod, true); err == nil { + t.Fatal("ordinary token admitted setup") + } +} + +func TestInstallationStepAuthenticatesBeforeStateReads(t *testing.T) { + h, a, _ := pendingStep(t) + store := &stepRaceStore{Store: h.store, at: 100, change: func() {}} + svc, err := authority.New(store, h.cfg) + if err != nil { + t.Fatal(err) + } + if _, _, err := svc.VerifyInstallationStep(h.ctx, "invalid", a.TargetID); err == nil { + t.Fatal("invalid token admitted") + } + if store.reads != 0 { + t.Fatal("unauthenticated state read") + } +} +func TestInstallationStepClockDelayCannotExtendLease(t *testing.T) { + for _, verify := range []bool{false, true} { + for _, delay := range []time.Duration{100 * time.Millisecond, 31 * time.Second} { + t.Run(fmt.Sprintf("verify=%v/delay=%s", verify, delay), func(t *testing.T) { + h, a, _ := pendingStep(t) + h.now = h.now.Add(950 * time.Millisecond) + started := h.now + issued := issueStep(t, h, a) + store := &stepRaceStore{Store: h.store, at: 2, change: func() { h.now = h.now.Add(delay) }} + svc, err := authority.New(store, h.cfg) + if err != nil { + t.Fatal(err) + } + if verify { + _, _, err = svc.VerifyInstallationStep(h.ctx, issued.Token, a.TargetID) + } else { + issued, err = svc.IssueInstallationStep(h.ctx, a, 30*time.Second) + } + if delay >= 30*time.Second { + if err == nil { + t.Fatal("expired lease admitted") + } + return + } + if err != nil { + t.Fatal(err) + } + if issued.Context.GetExpiresAtUnix() != started.Add(30*time.Second).Unix() { + t.Fatal("lease extended by state latency") + } + }) + } + } +} diff --git a/libraries/authority-core/go/authority/issue.go b/libraries/authority-core/go/authority/issue.go index 3a99e6f1..5b166304 100644 --- a/libraries/authority-core/go/authority/issue.go +++ b/libraries/authority-core/go/authority/issue.go @@ -392,6 +392,9 @@ func (s *Service) IssueForInstallation(ctx context.Context, w Workload, installa if inst.Status != domain.Active { return domain.NotNow("installation %s is %s: %s is not installed", inst.ID, inst.Status, inst.Target) } + if inst.AgentRole == domain.NoInstallationRole { + return domain.Refuse("installation %s declares no standing machine authority", inst.ID) + } if err = s.audienceOf(tx, &r, w.Module); err != nil { return err } diff --git a/libraries/authority-core/go/authority/lifecycle.go b/libraries/authority-core/go/authority/lifecycle.go index 074cd9e8..2addc30d 100644 --- a/libraries/authority-core/go/authority/lifecycle.go +++ b/libraries/authority-core/go/authority/lifecycle.go @@ -285,6 +285,10 @@ type InstallStep func(ctx context.Context, inst domain.Installation) error // while it is pending resumes it. Its principal is stable per organization and // target, so a reinstall reuses it; nobody holds a role until granted one, // the installing person included. The caller must administer the organization. +// A delivered installation-role selector is authoritative: a named role is +// selected unless the caller names that same role, and "none" selects no standing +// authority when the caller supplies no role. A caller may never supply "none". +// With no selector, the caller must name a declared role as before. func (s *Service) Install(ctx context.Context, caller Person, org, target, agentRole, installedBy string, approval *domain.ConsentApproval, step InstallStep) (domain.Installation, error) { if approval != nil { copy := *approval @@ -331,8 +335,9 @@ func (s *Service) install(ctx context.Context, authorize func(Tx) error, caller return domain.Missing("%q is not a composed solution; a module is installed by composition", target) } generation = m.Generation - if _, ok := m.Roles[agentRole]; !ok { - return domain.Missing("role %q is not one %s declares", agentRole, target) + selectedRole, err := installationRole(m, agentRole) + if err != nil { + return err } p, err := tx.Member(org, installedBy) if err != nil && !errors.Is(err, domain.ErrNotFound) { @@ -357,7 +362,7 @@ func (s *Service) install(ctx context.Context, authorize func(Tx) error, caller } } if err == nil && existing.Status == domain.Pending { - if existing.AgentRole != agentRole || existing.InstalledBy != installedBy { + if existing.AgentRole != selectedRole || existing.InstalledBy != installedBy { return domain.NotNow("%s is pending at %s as %s for %s; only that install resumes it", target, org, existing.AgentRole, existing.InstalledBy) } // The same install asked again resumes its pending attempt: its @@ -412,7 +417,7 @@ func (s *Service) install(ctx context.Context, authorize func(Tx) error, caller return err } inst = domain.Installation{ID: id, Organization: org, Target: target, Status: domain.Pending, Revision: existing.Revision + 1, - AgentRole: agentRole, PrincipalID: principal, InstalledBy: installedBy} + AgentRole: selectedRole, PrincipalID: principal, InstalledBy: installedBy} if approval != nil { inst.Consent = domain.ConsentReceipt{OperationID: approval.OperationID, Intent: intent, Revision: inst.Revision} } @@ -474,6 +479,28 @@ func (s *Service) install(ctx context.Context, authorize func(Tx) error, caller return inst, err } +// installationRole applies the signed declaration, not a preference derived from +// role order, human grants or bindings. The request is left unchanged for consent +// intent and transaction retries. "none" is a stored marker, not a role lookup. +func installationRole(m domain.Module, requested string) (string, error) { + if requested == domain.NoInstallationRole { + return "", domain.Refuse("none is an installation_role declaration, not a caller-supplied role") + } + selected := m.InstallationRole + if selected == "" { + selected = requested + } else if requested != "" && requested != selected { + return "", domain.Refuse("role %q cannot override %s's installation_role declaration", requested, m.Name) + } + if m.InstallationRole == domain.NoInstallationRole { + return domain.NoInstallationRole, nil + } + if _, ok := m.Roles[selected]; !ok { + return "", domain.Missing("role %q is not one %s declares", selected, m.Name) + } + return selected, nil +} + // requested is the journal entry of an installation attempt recorded pending. func requested(caller Person, inst domain.Installation) domain.Event { return domain.Event{Kind: domain.InstallationRequested, Organization: inst.Organization, Actor: caller.ID, Subject: inst.ID, diff --git a/libraries/authority-core/go/authority/roles.go b/libraries/authority-core/go/authority/roles.go index 767e3a4a..a533e6bf 100644 --- a/libraries/authority-core/go/authority/roles.go +++ b/libraries/authority-core/go/authority/roles.go @@ -59,12 +59,13 @@ func decideRoleChange(tx Tx, caller *Person, r RoleRequest, list, revoke bool) ( change.Subject = &p } } - if r.TeamID != "" { - people, err := tx.People(inst.Organization) + if r.TeamID != "" && !revoke && !list { + // Revocation remains possible after a team was deleted. A new grant + // requires the directory entry, not a person who happens to belong. + change.TeamOfOrganization, err = tx.TeamExists(inst.Organization, r.TeamID) if err != nil { return inst, err } - change.TeamOfOrganization = slices.ContainsFunc(people, func(p domain.Person) bool { return slices.Contains(p.Teams, r.TeamID) }) } return inst, decide.ChangeRole(change) } diff --git a/libraries/authority-core/go/authority/store.go b/libraries/authority-core/go/authority/store.go index 9b473184..9fd21f1d 100644 --- a/libraries/authority-core/go/authority/store.go +++ b/libraries/authority-core/go/authority/store.go @@ -35,6 +35,10 @@ type Tx interface { // teams there. A person who is not a member of it is ErrNotFound. Member(organization, id string) (domain.Person, error) People(organization string) ([]domain.Person, error) + // TeamExists reads the host's current team directory in this transaction, + // independently of membership. Empty teams exist; absent, deleted and + // other-organization teams answer false. Storage failures return an error. + TeamExists(organization, id string) (bool, error) OrgRole(organization, name string) (domain.OrgRole, error) // Module answers by principal, ModuleNamed by name; Modules lists every one. diff --git a/libraries/authority-core/go/authority/team_directory_test.go b/libraries/authority-core/go/authority/team_directory_test.go new file mode 100644 index 00000000..fa5134d7 --- /dev/null +++ b/libraries/authority-core/go/authority/team_directory_test.go @@ -0,0 +1,64 @@ +package authority_test + +import ( + "testing" + + "github.com/codefly-dev/authority/libraries/authority-core/go/authority" + "github.com/codefly-dev/authority/libraries/authority-core/go/domain" +) + +// A team is proven by the host's team directory, not by a member who happens +// to carry it: a team created a moment ago has nobody in it yet, and its role +// is granted before activation hands anyone access. +func TestARoleIsGrantedToATeamTheDirectoryHolds(t *testing.T) { + h := newHarness(t) + h.store.PutTeam(acme, "reviewers") // created inline, no member yet + h.store.PutTeam(globex, "auditors") + h.store.PutPerson(domain.Person{ID: "pat", Organization: acme, Teams: []string{"phantom"}}) // a membership with no directory entry + inst := h.inst(acme, "a").ID + grant := func(team string) error { + _, err := h.svc.GrantRole(h.ctx, h.person("alex"), authority.RoleRequest{Organization: acme, InstallationID: inst, Role: "viewer", TeamID: team}) + return err + } + + if err := grant("reviewers"); err != nil { + t.Fatalf("an empty team of the organization is refused: %v", err) + } + if _, err := h.svc.GrantRoleAsHost(h.ctx, authority.RoleRequest{Organization: acme, InstallationID: inst, Role: "author", TeamID: "reviewers"}); err != nil { + t.Fatalf("the host's setup cannot grant an empty team: %v", err) + } + if err := grant("team"); err != nil { + t.Fatalf("a team with members is refused: %v", err) + } + if _, err := h.root("tam", "a"); err != nil { + t.Fatalf("a member of the granted team holds no role: %v", err) + } + for name, team := range map[string]string{ + "a team of another organization": "auditors", + "a populated team of another organization": "ops", + "a team nobody created": "nobody", + "a team only a membership names": "phantom", + } { + t.Run(name, func(t *testing.T) { + err := grant(team) + refused(t, err, "not a team of") + code(t, err, domain.Denied) + }) + } + + // The first member joins after the grant and holds the team's role. + h.store.PutPerson(domain.Person{ID: "nia", Organization: acme, Teams: []string{"reviewers"}}) + if err := h.svc.PersonChanged(h.ctx, "nia"); err != nil { + t.Fatal(err) + } + if _, err := h.root("nia", "a"); err != nil { + t.Fatalf("a member who joined the granted team holds no role: %v", err) + } + + // A grant stays revocable after its team left the directory. + h.store.DeleteTeam(acme, "reviewers") + h.revoke("a", "viewer", "", "reviewers") + if err := grant("reviewers"); err == nil { + t.Fatal("a deleted team was granted") + } +} diff --git a/libraries/authority-core/go/decide/decide.go b/libraries/authority-core/go/decide/decide.go index c137347d..b0ea4dd3 100644 --- a/libraries/authority-core/go/decide/decide.go +++ b/libraries/authority-core/go/decide/decide.go @@ -229,7 +229,8 @@ type RoleChange struct { Role, PersonID, TeamID string // Subject is the person granted, when PersonID names one the host knows. Subject *domain.Person - // TeamOfOrganization: a person of the installation's organization belongs to TeamID. + // TeamOfOrganization: the host's team directory holds TeamID in the + // installation's organization, whether or not anyone belongs to it yet. TeamOfOrganization bool } diff --git a/libraries/authority-core/go/decide/decide_test.go b/libraries/authority-core/go/decide/decide_test.go index 8c4049e2..9850b697 100644 --- a/libraries/authority-core/go/decide/decide_test.go +++ b/libraries/authority-core/go/decide/decide_test.go @@ -152,11 +152,11 @@ func TestWhoMayChangeARoleInOrder(t *testing.T) { // A grant stays revocable after its person left, its team is gone, its // installation was uninstalled, or its role is no longer declared. for name, mutate := range map[string]func(*decide.RoleChange){ - "an uninstalled installation": func(r *decide.RoleChange) { r.Installation.Status = domain.Revoked }, - "a role no longer declared": func(r *decide.RoleChange) { r.Role = "retired" }, - "a person who left": func(r *decide.RoleChange) { r.Subject = nil }, - "a person now elsewhere": func(r *decide.RoleChange) { r.Subject = &omar }, - "a team no member carries": func(r *decide.RoleChange) { r.PersonID, r.TeamID = "", "gone" }, + "an uninstalled installation": func(r *decide.RoleChange) { r.Installation.Status = domain.Revoked }, + "a role no longer declared": func(r *decide.RoleChange) { r.Role = "retired" }, + "a person who left": func(r *decide.RoleChange) { r.Subject = nil }, + "a person now elsewhere": func(r *decide.RoleChange) { r.Subject = &omar }, + "a team no longer in the directory": func(r *decide.RoleChange) { r.PersonID, r.TeamID = "", "gone" }, } { t.Run("revoking "+name, func(t *testing.T) { r := base() diff --git a/libraries/authority-core/go/delivered/delivered.go b/libraries/authority-core/go/delivered/delivered.go index 69590154..921cd1fe 100644 --- a/libraries/authority-core/go/delivered/delivered.go +++ b/libraries/authority-core/go/delivered/delivered.go @@ -31,6 +31,9 @@ type Document struct { Methods []Method Roles []Role OwnerAware bool + // Core validates these declarations; every delivery hop carries them verbatim. + InstallationRole string + EdgeDependencies []domain.EdgeDependency } // Method is one method's declaration in the document. @@ -62,7 +65,7 @@ func VerifyDocument(document, envelope []byte, keys map[string]crypto.PublicKey, } // Pair holds a verified document to the carrier it was delivered with: the -// same module, the same generation, and the carrier's own digest. +// same module, generation, carrier digest and duplicated install declaration. func Pair(doc Document, carrierDocument []byte, manifest *solutionhost.ModuleManifest) error { switch { case doc.Module != manifest.Alias: @@ -72,7 +75,7 @@ func Pair(doc Document, carrierDocument []byte, manifest *solutionhost.ModuleMan case doc.CoreManifestDigest != delivery.Digest(carrierDocument): return domain.Refuse("the authority's document names another manifest than the one delivered with it") } - return nil + return sameInstallationDeclaration(manifest.Authority, &doc) } // Module is what a verified delivery composes: core's manifest (FromCore), @@ -80,6 +83,9 @@ func Pair(doc Document, carrierDocument []byte, manifest *solutionhost.ModuleMan // withdrawal carries no live declaration. func Module(manifest *solutionhost.ModuleManifest, doc *Document) (domain.Module, error) { m := FromCore(manifest) + if err := sameInstallationDeclaration(manifest.Authority, doc); err != nil { + return m, err + } if doc == nil { return m, nil } @@ -102,9 +108,35 @@ func Module(manifest *solutionhost.ModuleManifest, doc *Document) (domain.Module return m, err } m.OwnerAware = doc.OwnerAware + m.InstallationRole = doc.InstallationRole + m.EdgeDependencies = slices.Clone(doc.EdgeDependencies) return m, nil } +// sameInstallationDeclaration checks agreement, not declaration validity. Both +// signed carriers are projections of one authored value. Neither wins a conflict; +// a missing carrier supplies only the legacy empty declaration. +func sameInstallationDeclaration(carrier *solutionhost.Authority, doc *Document) error { + if carrier == nil { + carrier = &solutionhost.Authority{} + } + if doc == nil { + doc = &Document{} + } + if carrier.InstallationRole != doc.InstallationRole { + return domain.Refuse("installation_role differs between the signed manifest and authority document") + } + if len(carrier.EdgeDependencies) != len(doc.EdgeDependencies) { + return domain.Refuse("edge_dependencies differs between the signed manifest and authority document") + } + for i, edge := range carrier.EdgeDependencies { + if edge.Module != doc.EdgeDependencies[i].Module || edge.Presence != doc.EdgeDependencies[i].Presence { + return domain.Refuse("edge_dependencies differs between the signed manifest and authority document at entry %d", i) + } + } + return nil +} + // methods reads what each method declares. func methods(in []Method) (map[string]domain.MethodPolicy, error) { out := make(map[string]domain.MethodPolicy, len(in)) @@ -180,6 +212,10 @@ func FromCore(in *solutionhost.ModuleManifest) domain.Module { if in.Authority == nil { return m } + m.InstallationRole = in.Authority.InstallationRole + for _, edge := range in.Authority.EdgeDependencies { + m.EdgeDependencies = append(m.EdgeDependencies, domain.EdgeDependency{Module: edge.Module, Presence: edge.Presence}) + } if in.Authority.Principal != "" { m.Principal = in.Authority.Principal } diff --git a/libraries/authority-core/go/delivered/installation_declaration_test.go b/libraries/authority-core/go/delivered/installation_declaration_test.go new file mode 100644 index 00000000..03ea88b7 --- /dev/null +++ b/libraries/authority-core/go/delivered/installation_declaration_test.go @@ -0,0 +1,43 @@ +package delivered_test + +import ( + "github.com/codefly-dev/authority/libraries/authority-core/go/authority" + "github.com/codefly-dev/authority/libraries/authority-core/go/delivered" + "github.com/codefly-dev/authority/libraries/authority-core/go/domain" + "github.com/codefly-dev/authority/libraries/authority-core/go/offers" + modulecontract "github.com/codefly-dev/core/contracts/module" + "slices" + "testing" +) + +func TestInstallationDeclarationCarriageIsOpaqueAndIsolated(t *testing.T) { + // Whitespace is intentionally not Core-valid: this adapter must not become + // another parser or silently repair bytes already covered by a signature. + for _, selector := range []string{"", "none", "editor", " editor "} { + doc := &delivered.Document{InstallationRole: selector, EdgeDependencies: []domain.EdgeDependency{{Module: " module-id ", Presence: "required"}, {Module: "second", Presence: "optional"}}} + manifest := solutionManifest() + manifest.Authority.InstallationRole = selector + for _, edge := range doc.EdgeDependencies { + manifest.Authority.EdgeDependencies = append(manifest.Authority.EdgeDependencies, modulecontract.EdgeDependency{Module: edge.Module, Presence: edge.Presence}) + } + m, err := delivered.Module(manifest, doc) + if err != nil { + t.Fatal(err) + } + review, err := offers.Project(authority.ReviewedOffer{Module: m}, nil) + if err != nil { + t.Fatal(err) + } + if m.InstallationRole != selector || review.InstallationRole != selector || !slices.Equal(doc.EdgeDependencies, review.EdgeDependencies) { + t.Fatal("declaration transformed") + } + if len(review.Ceiling) != 0 || len(review.Roles) != 0 { + t.Fatal("edges created authority") + } + doc.EdgeDependencies[0].Module = "changed" + review.EdgeDependencies[1].Module = "changed" + if m.EdgeDependencies[0].Module != " module-id " || m.EdgeDependencies[1].Module != "second" { + t.Fatal("carrier aliases declaration") + } + } +} diff --git a/libraries/authority-core/go/delivered/installation_pairing_test.go b/libraries/authority-core/go/delivered/installation_pairing_test.go new file mode 100644 index 00000000..3e5c904d --- /dev/null +++ b/libraries/authority-core/go/delivered/installation_pairing_test.go @@ -0,0 +1,93 @@ +package delivered_test + +import ( + "strings" + "testing" + + "github.com/codefly-dev/authority/libraries/authority-core/go/delivered" + "github.com/codefly-dev/authority/libraries/authority-core/go/domain" + delivery "github.com/codefly-dev/authority/libraries/delivery/go" + modulecontract "github.com/codefly-dev/core/contracts/module" + "github.com/codefly-dev/core/solutionhost" +) + +// These are shared-reader tests, not the CLI-generated signed protobuf path. +// Existing signature tests cover authentication; here all identity/digest +// pairing fields agree so each negative isolates the duplicated declaration. +func TestInstallationDeclarationPairingRefusesEitherCarrierMismatch(t *testing.T) { + for _, tc := range []struct { + name, field string + change func(*solutionhost.ModuleManifest, **delivered.Document) + }{ + {"selector", "installation_role", func(m *solutionhost.ModuleManifest, d **delivered.Document) { (*d).InstallationRole = "editor" }}, + {"sidecar selector absent", "installation_role", func(m *solutionhost.ModuleManifest, d **delivered.Document) { (*d).InstallationRole = "" }}, + {"core selector absent", "installation_role", func(m *solutionhost.ModuleManifest, d **delivered.Document) { m.Authority.InstallationRole = "" }}, + {"sidecar absent", "installation_role", func(m *solutionhost.ModuleManifest, d **delivered.Document) { *d = nil }}, + {"core authority absent", "installation_role", func(m *solutionhost.ModuleManifest, d **delivered.Document) { m.Authority = nil }}, + {"edge identity", "edge_dependencies", func(m *solutionhost.ModuleManifest, d **delivered.Document) { + (*d).EdgeDependencies[0].Module = "different" + }}, + {"edge presence", "edge_dependencies", func(m *solutionhost.ModuleManifest, d **delivered.Document) { + (*d).EdgeDependencies[0].Presence = "optional" + }}, + {"edge order", "edge_dependencies", func(m *solutionhost.ModuleManifest, d **delivered.Document) { + (*d).EdgeDependencies[0], (*d).EdgeDependencies[1] = (*d).EdgeDependencies[1], (*d).EdgeDependencies[0] + }}, + {"sidecar edges absent", "edge_dependencies", func(m *solutionhost.ModuleManifest, d **delivered.Document) { (*d).EdgeDependencies = nil }}, + {"core edges absent", "edge_dependencies", func(m *solutionhost.ModuleManifest, d **delivered.Document) { m.Authority.EdgeDependencies = nil }}, + } { + t.Run(tc.name, func(t *testing.T) { + m := solutionManifest() + m.Authority.InstallationRole = "none" + m.Authority.EdgeDependencies = []modulecontract.EdgeDependency{{Module: "z", Presence: "required"}, {Module: "a", Presence: "optional"}} + carrier := []byte("verified carrier bytes") + doc := &delivered.Document{Module: m.Alias, Generation: m.Generation, CoreManifestDigest: delivery.Digest(carrier), InstallationRole: "none", EdgeDependencies: []domain.EdgeDependency{{Module: "z", Presence: "required"}, {Module: "a", Presence: "optional"}}} + if err := delivered.Pair(*doc, carrier, m); err != nil { + t.Fatal("positive control", err) + } + if _, err := delivered.Module(m, doc); err != nil { + t.Fatal("positive control", err) + } + tc.change(m, &doc) + if doc != nil { + err := delivered.Pair(*doc, carrier, m) + code(t, err, domain.Denied) + if !strings.Contains(err.Error(), tc.field) { + t.Fatal(err) + } + } + _, err := delivered.Module(m, doc) + code(t, err, domain.Denied) + if !strings.Contains(err.Error(), tc.field) { + t.Fatal(err) + } + }) + } +} + +func TestInstallationDeclarationPairingKeepsLegacyAbsence(t *testing.T) { + for _, authority := range []*solutionhost.Authority{nil, {}, {EdgeDependencies: []modulecontract.EdgeDependency{}}} { + for _, doc := range []*delivered.Document{nil, {}, {EdgeDependencies: []domain.EdgeDependency{}}} { + m := solutionManifest() + m.Authority = authority + got, err := delivered.Module(m, doc) + if err != nil || got.InstallationRole != "" || len(got.EdgeDependencies) != 0 { + t.Fatalf("legacy: %+v %v", got, err) + } + } + } +} + +func TestInstallationDeclarationFromCorePreservesBytesWithoutAliasing(t *testing.T) { + m := solutionManifest() + m.Authority.InstallationRole = "none" + m.Authority.EdgeDependencies = []modulecontract.EdgeDependency{{Module: "z", Presence: "required"}, {Module: "a", Presence: "optional"}} + got := delivered.FromCore(m) + if got.InstallationRole != "none" || got.EdgeDependencies[0].Module != "z" || got.EdgeDependencies[1].Module != "a" { + t.Fatalf("projection: %+v", got) + } + m.Authority.EdgeDependencies[0].Module = "changed" + if got.EdgeDependencies[0].Module != "z" { + t.Fatal("projection aliases signed manifest") + } +} diff --git a/libraries/authority-core/go/domain/domain.go b/libraries/authority-core/go/domain/domain.go index cd335010..cb141f10 100644 --- a/libraries/authority-core/go/domain/domain.go +++ b/libraries/authority-core/go/domain/domain.go @@ -66,6 +66,13 @@ type Module struct { Roles map[string][]*basev0.WorkScopeV1 // OwnedRoles resolve only to the exact ids attached to this installation. OwnedRoles map[string][]*basev0.WorkScopeV1 + // InstallationRole is the signed selector: absent leaves selection to the + // caller; "none" selects no standing authority; otherwise it names a role. + // Omit absent fields from the existing review digest's JSON representation. + InstallationRole string `json:",omitempty"` + // EdgeDependencies declares viewer-edge presence, never authority or grants. + // Nil and empty are equivalent, including in the review digest. + EdgeDependencies []EdgeDependency `json:",omitempty"` // OwnerAware: the module compares a resource's owner with an // owner-selected scope, so it may receive one. OwnerAware bool @@ -74,6 +81,17 @@ type Module struct { Methods map[string]MethodPolicy } +// NoInstallationRole is declaration-only. It is retained on an installation +// as a no-standing-authority marker, never looked up as a role or granted. +const NoInstallationRole = "none" + +// EdgeDependency is a Core-validated viewer-edge presence requirement. Module +// is the exact signed Module.Principal. Hosts resolve it through their catalogue +// to a live target; it is not a target UUID, route alias or binding id. +type EdgeDependency struct { + Module, Presence string +} + // MethodPolicy is one method's declaration: exactly one of its fields is set. type MethodPolicy struct { Requires []*basev0.WorkScopeV1 @@ -101,7 +119,8 @@ const ( // Installation is one organization's installation of one target. A // solution's installation is a principal (PrincipalID): its data is owned by -// it and it acts as it for itself, holding AgentRole. +// it and it acts as it for itself, holding AgentRole unless it is the +// declaration-only NoInstallationRole marker (no standing authority). type Installation struct { ID, Organization, Target string Status Status diff --git a/libraries/authority-core/go/go.mod b/libraries/authority-core/go/go.mod index d9ae5158..41501172 100644 --- a/libraries/authority-core/go/go.mod +++ b/libraries/authority-core/go/go.mod @@ -6,7 +6,7 @@ require ( github.com/codefly-dev/authority/libraries/delivery/go v0.0.0-20261026023425-000d44495d93 github.com/codefly-dev/authority/libraries/hop/go v0.0.0-20261026193714-49101ae11a7f github.com/codefly-dev/authority/libraries/resources/go v0.0.0-20261026023425-000d44495d93 - github.com/codefly-dev/core v0.19.7-0.20261026092055-b23dc13543b2 + github.com/codefly-dev/core v0.19.17-0.20261029043538-2aa2e9aaf950 github.com/google/uuid v1.6.0 google.golang.org/protobuf v1.36.12 ) @@ -19,13 +19,13 @@ require ( github.com/Masterminds/semver v1.5.0 // indirect github.com/Masterminds/semver/v3 v3.5.0 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect - github.com/go-openapi/analysis v1.0.0 // indirect + github.com/go-openapi/analysis v1.0.1 // indirect github.com/go-openapi/errors v0.22.9 // indirect - github.com/go-openapi/jsonpointer v1.0.1 // indirect - github.com/go-openapi/jsonreference v1.0.2 // indirect + github.com/go-openapi/jsonpointer v1.0.2 // indirect + github.com/go-openapi/jsonreference v1.0.3 // indirect github.com/go-openapi/loads v0.25.3 // indirect github.com/go-openapi/spec v1.0.1 // indirect - github.com/go-openapi/strfmt v0.27.2 // indirect + github.com/go-openapi/strfmt v0.27.4 // indirect github.com/go-openapi/swag/conv v0.29.2 // indirect github.com/go-openapi/swag/jsonutils v0.29.2 // indirect github.com/go-openapi/swag/loading v0.29.2 // indirect @@ -46,13 +46,13 @@ require ( github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 // indirect github.com/yoheimuta/go-protoparser/v4 v4.14.2 // indirect go.yaml.in/yaml/v3 v3.0.5 // indirect - golang.org/x/exp v0.0.0-20260908205506-85c1c2202aba // indirect - golang.org/x/net v0.60.0 // indirect + golang.org/x/exp v0.0.0-20261014143641-f0251eb4aa3e // indirect + golang.org/x/net v0.62.0 // indirect golang.org/x/sys v0.49.0 // indirect - golang.org/x/term v0.46.0 // indirect - golang.org/x/text v0.42.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20261019180522-ce908d29299b // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20261012220534-4341edf3f941 // indirect - google.golang.org/grpc v1.84.0 // indirect + golang.org/x/term v0.47.0 // indirect + golang.org/x/text v0.43.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20261026165753-2ffab32bce7e // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20261026165753-2ffab32bce7e // indirect + google.golang.org/grpc v1.85.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/libraries/authority-core/go/go.sum b/libraries/authority-core/go/go.sum index f1dc59c3..5067c98d 100644 --- a/libraries/authority-core/go/go.sum +++ b/libraries/authority-core/go/go.sum @@ -12,6 +12,7 @@ github.com/Masterminds/semver/v3 v3.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAw github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/Microsoft/go-winio v0.6.3-0.20251027160822-ad3df93bed29 h1:0kQAzHq8vLs7Pptv+7TxjdETLf/nIqJpIB4oC6Ba4vY= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= @@ -36,6 +37,8 @@ github.com/codefly-dev/authority/libraries/resources/go v0.0.0-20261026023425-00 github.com/codefly-dev/authority/libraries/resources/go v0.0.0-20261026023425-000d44495d93/go.mod h1:1rmjrVyr0h7zOuwOHS9i+BoeS9dr3XNvtupLZFwY6As= github.com/codefly-dev/core v0.19.7-0.20261026092055-b23dc13543b2 h1:I1wL0ANXhTdQuEAnxXF+s4+rKILlBu/QHILq4Skyi3I= github.com/codefly-dev/core v0.19.7-0.20261026092055-b23dc13543b2/go.mod h1:1ina3kP+YSAL4tlZvnEYd4P8bFGnJECfkP86ojeeF1A= +github.com/codefly-dev/core v0.19.17-0.20261029043538-2aa2e9aaf950 h1:U5HeCDUfftwVFbMYN1HGPUL+9HO+ske3nkExu1C2kYY= +github.com/codefly-dev/core v0.19.17-0.20261029043538-2aa2e9aaf950/go.mod h1:Q7PHKw8SFb1wNxlTy2EgG11cI9Zmq55TYY1fdsQUhpE= github.com/codefly-dev/gortk v0.2.0 h1:7bOlS5valYz2zil+fZctQNcPCYBcPj86abcw9N8h1hQ= github.com/codefly-dev/gortk v0.2.0/go.mod h1:dDWUMFgAP063OCGhTEpV7FFUj9+zTcxxO/nV80VKEwg= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= @@ -70,18 +73,26 @@ github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-openapi/analysis v1.0.0 h1:sNvbAGCJqUTqIAodr9IVqJMmuZas3YS9ms1dGK9yiJ4= github.com/go-openapi/analysis v1.0.0/go.mod h1:NhYjJ57fnE+bcE7UwrJyMkhWA3Dfz7TdiBfTVAnos4Y= +github.com/go-openapi/analysis v1.0.1 h1:JTEyujgl4GyFj8pbMUY2yZx5ak1Ozt/DaqzQjQBnBWM= +github.com/go-openapi/analysis v1.0.1/go.mod h1:f3gYbXmnXSQIZB02DqE4f8JX969PdOEnaY7gDBLeJjI= github.com/go-openapi/errors v0.22.9 h1:HI9+SyVYiRzyeBQGv0CbWbQa+u2S0nny7G7AhY+UMiM= github.com/go-openapi/errors v0.22.9/go.mod h1:R73q66sXP2smzAGgFOI4nqtusRK2xN5pYCjsij+KX58= github.com/go-openapi/jsonpointer v1.0.1 h1:2KxywRmNwJkT/FMBa3iRNHEaAxSJvjqoufQZy3au1Mg= github.com/go-openapi/jsonpointer v1.0.1/go.mod h1:wI7ZYsFmbIi9nBXOZqgDaS/bqOchRGZjqxFli7FBYxY= +github.com/go-openapi/jsonpointer v1.0.2 h1:NbW7xT2b+l7x+JefiPGDsZWsrLk1ogrz4AHK1TTzMt8= +github.com/go-openapi/jsonpointer v1.0.2/go.mod h1:N0rI4MbNKD7xAFTiHtB+5Nhrg2j/wwYg7m7Fmb4/jk8= github.com/go-openapi/jsonreference v1.0.2 h1:oS4et8FOf3p3UQxEo4Xt0esijmBUM+F259Xl72OSZsc= github.com/go-openapi/jsonreference v1.0.2/go.mod h1:TbUNSOo+fcorZjFaNoiSDSoaNnnZtqJtLGR1PuvE/Cs= +github.com/go-openapi/jsonreference v1.0.3 h1:5rDbq+JsjCv6/nUDDJt83nLZoADI4w03uvHXer+4aYQ= +github.com/go-openapi/jsonreference v1.0.3/go.mod h1:OJSlMXHysgH272D9z2tiy2MGe5A9vQLHaCPb6KbYdE4= github.com/go-openapi/loads v0.25.3 h1:V+jKy/thXWdLJUuYC8sZX2dICyAa8M3DokF70jj34P0= github.com/go-openapi/loads v0.25.3/go.mod h1:LgLyCSOLBL2Qnj0Ps1oo2YH8jZoEKsMeVZCc+ALIxFo= github.com/go-openapi/spec v1.0.1 h1:lj2vdGpNDcVgwRc6qXdw6qt/KQpCtSa9tnUH6vpDPDk= github.com/go-openapi/spec v1.0.1/go.mod h1:M//GWQGtDUAjnP37gE6fInLgaczB+FatoipV3H1fYw8= github.com/go-openapi/strfmt v0.27.2 h1:SG32SlbwNy92s0KJiVxt2joJeFdqIYHvwrA0OU6HqzQ= github.com/go-openapi/strfmt v0.27.2/go.mod h1:M4CKsMO0Fb8qR10+1Ra75wCKNNquy+Vj+4LWZrhTo2E= +github.com/go-openapi/strfmt v0.27.4 h1:E1xsvHb8R9vsuYru4TP2PVVTv9AIgSFAUACUZrFQfCI= +github.com/go-openapi/strfmt v0.27.4/go.mod h1:SCOff81PPhU26acBiW96jHaFycEbT2nkQRvojM+V2z0= github.com/go-openapi/swag v0.29.2 h1:9n8frkcsuQRA0INU31VbxNxt1zBaHxrHYGEf5BUjFi4= github.com/go-openapi/swag v0.29.2/go.mod h1:RkCiX1gCVXWgLOavDXE3ALJrQ/VO2ziiIzOUs3z10xo= github.com/go-openapi/swag/cmdutils v0.29.2 h1:cXEzX/nWCODon251f+1HgL75/R4XXML5IZxgLCWMoro= @@ -187,6 +198,7 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.7 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.72.0/go.mod h1:A+EVhDakAj1waJBPhmTszZaSr08ww8MRRKy1nB1rqck= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0 h1:3g7B90UzBltIDKq1/5mrTGxTnOFDV0ICOhLoxiZ8jlg= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0/go.mod h1:Ef8SuTh59BT7+ofpDxN9z+yOlc4t2GjLmKDgYNJL/NU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.72.0 h1:LxwW/9ctSCv+QkE/cLR7M91ZIkXNMqJtEMi1vCw9U8s= go.opentelemetry.io/otel v1.47.0 h1:j7ALJ/zgkS7Z6aeJW09p8VC9804bC+PpeTfCD4XPnOM= go.opentelemetry.io/otel v1.47.0/go.mod h1:8wS9O2qfXrYrzp6hIF/HOYJJf/wIhFPhR2xLuP+iXQU= go.opentelemetry.io/otel/log v1.47.0 h1:cOTS1CcLbSQeZKanGJ+0JpF/+t4PELi3O3bbl2lqCcI= @@ -201,22 +213,36 @@ go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/exp v0.0.0-20260908205506-85c1c2202aba h1:Ck8QetSgk912qxWLMCKxd0in+aiyBQyDSMae6e/xmpU= golang.org/x/exp v0.0.0-20260908205506-85c1c2202aba/go.mod h1:50RgIsmK7OwqzTTeqcSXQW8SswW0o8fRcDxmqGluJ8E= +golang.org/x/exp v0.0.0-20261014143641-f0251eb4aa3e h1:HgI6W1OolKK6tcV74is+rl9SMT0njsHlp0TbQiGXI/A= +golang.org/x/exp v0.0.0-20261014143641-f0251eb4aa3e/go.mod h1:DBoxwWhjaW8DJJw/YeM4zpeOKMC1FMXSblsYy+1mFSw= golang.org/x/net v0.60.0 h1:79p50tfZlm0J9YfoDsSi639qSXNGVwEzOPLCxM2FsYU= golang.org/x/net v0.60.0/go.mod h1:2DA/G1UfVbCpQPeWTmMPGY7Cs2PkBkwu743bVX5PIVg= +golang.org/x/net v0.62.0 h1:18ELalfM5+XNRwzM2nYqXsZQ9ByS1uhj2O/U7+5jqRY= +golang.org/x/net v0.62.0/go.mod h1:WgDCOTH8iwtB66T4/GfI94AHo/hhdb7Hah9NDseQfiI= golang.org/x/sys v0.49.0 h1:XbzkgYJHdqh/8m2Uu0W/dQv8nktxx4BFHp1M0gROTXA= golang.org/x/sys v0.49.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= golang.org/x/term v0.46.0 h1:3+OXuTbaKDgwk8jTi3aSLHRlmWqHEUDUtxnbFigO4YE= golang.org/x/term v0.46.0/go.mod h1:+K02xbkittuwc0Am4abfA3Fc+XRGXkvBXNO88NCXPoc= +golang.org/x/term v0.47.0 h1:cpyOwQkr2Nzxe2p6KnrTG7Ei2wtzjsuSjhpXXepZ/BM= +golang.org/x/term v0.47.0/go.mod h1:efAfAuG6RKABvg1ArGebRkvX6LXul14SM67o2e4mUCY= golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI= golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E= +golang.org/x/text v0.43.0 h1:1QivrlhwAsnMOcqgOdl9my7s9OyZl8O5/UGUTJVXoWc= +golang.org/x/text v0.43.0/go.mod h1:hbSIYA/amXcRXTFSZqh8tWHEoqRvvbKPJ2uz/KU+a+s= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/genproto/googleapis/api v0.0.0-20261019180522-ce908d29299b h1:nB3spRIaSPtxJpxb0k41yljROvm2Jrwfs2pSNdKS5y8= google.golang.org/genproto/googleapis/api v0.0.0-20261019180522-ce908d29299b/go.mod h1:9N4vtjEwvgQKEdlFVQO0M44ErhecQ9DCSQfGoZ51M6w= +google.golang.org/genproto/googleapis/api v0.0.0-20261026165753-2ffab32bce7e h1:e789sLZjeTdc02ZCQGTSzHOu0fR73+dwPQVKUp67Z88= +google.golang.org/genproto/googleapis/api v0.0.0-20261026165753-2ffab32bce7e/go.mod h1:vHV3wJM9WZyfV7GyB+OQGq5dWVrFwCDxapPK4WlRD88= google.golang.org/genproto/googleapis/rpc v0.0.0-20261012220534-4341edf3f941 h1:QG6hS4zFplRZvOQedzei4t2pT/zkQc3VR1u+S9/rcAw= google.golang.org/genproto/googleapis/rpc v0.0.0-20261012220534-4341edf3f941/go.mod h1:OaIUM3+LpYcK2GXM4FTmhWoIq371Owdr+Cc7/BsYHHc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20261026165753-2ffab32bce7e h1:w3fy/G8OrJHYexn+u5y7hFyc8YRRIr7DCevtIOwAcGY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20261026165753-2ffab32bce7e/go.mod h1:OaIUM3+LpYcK2GXM4FTmhWoIq371Owdr+Cc7/BsYHHc= google.golang.org/grpc v1.84.0 h1:soMyaPJ8pAak5PIQ0DGBUir0XRo2fRoMqhNWMLlLxO0= google.golang.org/grpc v1.84.0/go.mod h1:ljCht0DrxQrXBDRTZp52Qxh3Ffk8CdYm2sj4O2QN2C0= +google.golang.org/grpc v1.85.0 h1:BPBCfuVRQ1qvBhKDOq9uvQllMvO59rKsYhXpp7Dd8vw= +google.golang.org/grpc v1.85.0/go.mod h1:0rQrOD58yjal/0EaVbGBANindixsCZNQPDV3VKtaKuQ= google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc= google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= diff --git a/libraries/authority-core/go/memstore/memstore.go b/libraries/authority-core/go/memstore/memstore.go index 78f2a2a3..a578553a 100644 --- a/libraries/authority-core/go/memstore/memstore.go +++ b/libraries/authority-core/go/memstore/memstore.go @@ -22,6 +22,7 @@ type Store struct { mu sync.RWMutex orgs map[string]domain.Organization people map[string]map[string]domain.Person // id → organization → membership + teams map[string]map[string]bool // organization → team id roles map[string]domain.OrgRole // organization/name modules map[string]domain.Module // name installations map[string]domain.Installation @@ -42,7 +43,7 @@ func New() *Store { modules: map[string]domain.Module{}, installations: map[string]domain.Installation{}, grants: map[string][]domain.RoleGrant{}, revisions: map[string]uint64{}, personEpochs: map[string]uint64{}, grantEpochs: map[string]uint64{}, ownerAware: map[string]uint64{}, delegations: map[string]domain.Delegation{}, tombstones: map[string]domain.Tombstone{}, - clients: map[string]domain.Client{}, + clients: map[string]domain.Client{}, teams: map[string]map[string]bool{}, } } @@ -113,6 +114,24 @@ func (s *Store) PutOrganization(o domain.Organization) { s.orgs[o.ID] = o } +// PutTeam records an existing team, whether or not it has members. +func (s *Store) PutTeam(organization, id string) { + s.mu.Lock() + defer s.mu.Unlock() + if s.teams[organization] == nil { + s.teams[organization] = map[string]bool{} + } + s.teams[organization][id] = true +} + +// DeleteTeam removes a directory entry. The host owns membership cleanup and +// the corresponding PersonChanged notifications; this creates no memberships. +func (s *Store) DeleteTeam(organization, id string) { + s.mu.Lock() + defer s.mu.Unlock() + delete(s.teams[organization], id) +} + // PutPerson records a person's membership of p.Organization; a person may // be a member of several organizations, each recorded by its own call. func (s *Store) PutPerson(p domain.Person) { @@ -208,6 +227,10 @@ func (t tx) People(org string) ([]domain.Person, error) { return out, nil } +func (t tx) TeamExists(organization, id string) (bool, error) { + return t.s.teams[organization][id], nil +} + func (t tx) OrgRole(org, name string) (domain.OrgRole, error) { r, ok := t.s.roles[org+"/"+name] if !ok { @@ -428,6 +451,7 @@ func cloneScopes(in []*basev0.WorkScopeV1) []*basev0.WorkScopeV1 { } func cloneModule(m domain.Module) domain.Module { + m.EdgeDependencies = slices.Clone(m.EdgeDependencies) m.ResourceKinds = cloneScopes(m.ResourceKinds) bindings := make(map[string]domain.Binding, len(m.Bindings)) for id, b := range m.Bindings { diff --git a/libraries/authority-core/go/offers/offers.go b/libraries/authority-core/go/offers/offers.go index e9422d89..61ffec6e 100644 --- a/libraries/authority-core/go/offers/offers.go +++ b/libraries/authority-core/go/offers/offers.go @@ -19,12 +19,14 @@ import ( // Review is presentation of an already-authorized, consistent core read. // Approval is absent on a member's read; projection never grants consent. type Review struct { - Target string - Approval *domain.ConsentApproval - Installation *domain.Installation - Ceiling []*Binding - Roles []*Role - ToolsComposed bool + Target string + Approval *domain.ConsentApproval + Installation *domain.Installation + Ceiling []*Binding + Roles []*Role + ToolsComposed bool + InstallationRole string + EdgeDependencies []domain.EdgeDependency } type Binding struct { @@ -62,7 +64,8 @@ type Scope struct { func Project(offer authority.ReviewedOffer, bindings map[string]*runnablev0.PreparedBinding) (*Review, error) { m, inst := offer.Module, offer.Installation a := offer.Approval - out := &Review{Target: m.Name, ToolsComposed: len(bindings) > 0} + out := &Review{Target: m.Name, ToolsComposed: len(bindings) > 0, + InstallationRole: m.InstallationRole, EdgeDependencies: slices.Clone(m.EdgeDependencies)} // A member's read carries no consent tuple: only an administrator consents. if a.Digest != "" { out.Approval = &domain.ConsentApproval{TargetID: a.TargetID, Generation: a.Generation, ExpectedRevision: a.ExpectedRevision, Digest: a.Digest} diff --git a/libraries/authority-core/go/storetest/memstore_test.go b/libraries/authority-core/go/storetest/memstore_test.go index 995bb54a..5a85b5fe 100644 --- a/libraries/authority-core/go/storetest/memstore_test.go +++ b/libraries/authority-core/go/storetest/memstore_test.go @@ -16,6 +16,9 @@ func TestTheMemoryStoreMeetsTheStoreContract(t *testing.T) { for _, o := range id.Organizations { s.PutOrganization(o) } + for _, team := range id.Teams { + s.PutTeam(team.Organization, team.ID) + } for _, m := range id.Members { p := domain.Person{ID: m.Person, Organization: m.Organization, Teams: m.Teams} if m.Administrator { diff --git a/libraries/authority-core/go/storetest/storetest.go b/libraries/authority-core/go/storetest/storetest.go index 5bec6798..9826228f 100644 --- a/libraries/authority-core/go/storetest/storetest.go +++ b/libraries/authority-core/go/storetest/storetest.go @@ -31,6 +31,12 @@ import ( type Identity struct { Organizations []domain.Organization Members []Member + Teams []Team +} + +// Team is an authoritative directory entry, independent of member rows. +type Team struct { + Organization, ID string } // Member is one person's membership of one organization: an administrator @@ -65,6 +71,26 @@ func Run(t *testing.T, open Open) { } } + t.Run("team directory includes empty teams and refuses foreign or absent ids", func(t *testing.T) { + read(t, func(tx authority.Tx) error { + for _, team := range f.identity.Teams { + ok, err := tx.TeamExists(team.Organization, team.ID) + if err != nil || !ok { + t.Errorf("team %+v: exists=%t error=%v", team, ok, err) + } + ok, err = tx.TeamExists(f.orgB, team.ID) + if err != nil || ok { + t.Errorf("foreign team %+v: exists=%t error=%v", team, ok, err) + } + } + ok, err := tx.TeamExists(f.orgA, uuid.NewString()) + if err != nil || ok { + t.Errorf("absent team: exists=%t error=%v", ok, err) + } + return nil + }) + }) + t.Run("identity reads answer what the host seeded", func(t *testing.T) { read(t, func(tx authority.Tx) error { all, err := tx.Organizations() @@ -458,6 +484,7 @@ func newFixture() *fixture { reader := []domain.RoleScope{{Kind: "storetest.item", Actions: []string{"read"}}, {Kind: "storetest.note", Actions: []string{"write"}, Resources: []resources.Grant{{ID: "note-1"}}}} f.identity = Identity{ Organizations: []domain.Organization{{ID: f.orgA, Name: "Storetest A " + f.run}, {ID: f.orgB, Name: "Storetest B " + f.run}}, + Teams: []Team{{Organization: f.orgA, ID: f.team}, {Organization: f.orgA, ID: uuid.NewString()}}, Members: []Member{ {Organization: f.orgA, Person: f.onlyA, Teams: []string{f.team}, Scopes: reader}, {Organization: f.orgA, Person: f.both, Administrator: true}, diff --git a/libraries/conformance/go/solution_principal.go b/libraries/conformance/go/solution_principal.go index 2039256c..8ec4c5b8 100644 --- a/libraries/conformance/go/solution_principal.go +++ b/libraries/conformance/go/solution_principal.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/codefly-dev/core/solutionhost" + "github.com/google/uuid" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/protobuf/reflect/protoreflect" @@ -65,6 +66,9 @@ import ( // - Viewer, Editor, Member, Installer, TeamMember, TeamLeaver, OrgLeaver: // members with no role on A, B or C but what the suite grants; TeamMember // and TeamLeaver belong to Team. +// - EmptyTeam: a team of Organization in the host's team directory that +// nobody belongs to: a team exists because the host holds it, not because +// a member carries it. // - Administrator holds admin, and Reader holds read, on Existing from their // own organization grants at Store, and nothing else of Kind. // - NoteReader holds read on A's own kind (".note") from their own @@ -82,6 +86,7 @@ type SolutionPrincipalCase struct { Generation uint64 Admin, Viewer, Editor, Member, Installer string Team, TeamMember, TeamLeaver, OrgLeaver string + EmptyTeam string Administrator, Reader, NoteReader string OtherAdmin, OtherMember, OtherTeam string Existing string @@ -162,7 +167,7 @@ func RunSolutionPrincipalTarget(t *testing.T, conn grpc.ClientConnInterface, des t.Fatalf("target: the published contract %s", strings.Join(problems, "; ")) } if f.Uninstall == nil || f.RemoveFromTeam == nil || f.RemoveFromOrganization == nil || f.SignManifest == nil || f.SignAuthority == nil || - f.DeliveryCredential == nil || f.Existing == "" || f.OtherOrganization == "" || f.LegacyStore == "" || f.Team == "" || f.OtherTeam == "" || f.C.Solution == "" || + f.DeliveryCredential == nil || f.Existing == "" || f.OtherOrganization == "" || f.LegacyStore == "" || f.Team == "" || f.EmptyTeam == "" || f.OtherTeam == "" || f.C.Solution == "" || slices.Contains([]string{f.Admin, f.Viewer, f.Editor, f.Member, f.Installer, f.TeamMember, f.TeamLeaver, f.OrgLeaver, f.Administrator, f.Reader, f.NoteReader, f.OtherAdmin, f.OtherMember}, "") { t.Fatal("required conformance fixture missing: solution principal case") } @@ -366,6 +371,7 @@ func RunSolutionPrincipalTarget(t *testing.T, conn grpc.ClientConnInterface, des "neither a person nor a team": {f.Admin, "GrantRole", grant(instA, "viewer", nil), codes.InvalidArgument, "exactly one"}, "a person of another organization": {f.Admin, "GrantRole", grant(instA, "viewer", sp.person(t, f.OtherMember)), codes.PermissionDenied, "not a member"}, "a team of another organization": {f.Admin, "GrantRole", grant(instA, "viewer", map[string]any{"team_id": f.OtherTeam}), codes.PermissionDenied, "not a team of"}, + "a team the host does not hold": {f.Admin, "GrantRole", grant(instA, "viewer", map[string]any{"team_id": uuid.NewString()}), codes.PermissionDenied, "not a team of"}, } { t.Run(name, func(t *testing.T) { _, err := c.call(sp.as(t, r.caller), "host.installation.v1.InstallationService", r.method, r.fields) @@ -526,6 +532,17 @@ func RunSolutionPrincipalTarget(t *testing.T, conn grpc.ClientConnInterface, des _, err = sp.root(t, f.TeamLeaver, A) refused(t, err, codes.PermissionDenied, "holds no role") }) + t.Run("a team nobody belongs to yet is granted a role, listed and revoked", func(t *testing.T) { + empty := map[string]any{"team_id": f.EmptyTeam} + sp.grant(t, A, "viewer", empty) + if !slices.Contains(sp.roleGrants(t, f.Admin, f.Organization, A), "viewer team "+f.EmptyTeam) { + t.Fatalf("ListRoleGrants does not show the empty team's grant: %v", sp.roleGrants(t, f.Admin, f.Organization, A)) + } + sp.revoke(t, A, "viewer", empty) + if slices.Contains(sp.roleGrants(t, f.Admin, f.Organization, A), "viewer team "+f.EmptyTeam) { + t.Fatal("ListRoleGrants still shows the empty team's revoked grant") + } + }) t.Run("a member lists exactly the installations they hold a role on, directly or through a team; holding nothing is an empty list; a revoke or leaving the team drops one at once; another organization's never appear", func(t *testing.T) { sp.heldInstallations(t) }) diff --git a/modules/authority/contracts/api/catalog.codefly.json b/modules/authority/contracts/api/catalog.codefly.json index 12c9922c..d30cff5d 100644 --- a/modules/authority/contracts/api/catalog.codefly.json +++ b/modules/authority/contracts/api/catalog.codefly.json @@ -43,7 +43,7 @@ "kind": "protobuf", "package": "", "path": "contracts/api/local/grpc/contract.binpb", - "digest": "sha256:2d74a1ad05ecab544606e2e52630cc5cb6c16729371f091262dfb45a686d2712", + "digest": "sha256:b49a5399e647c61b1991fc22bf7fcf8262ab17333dbe01b4afd1d16c346f3ea7", "services": [ { "name": "LocalService", diff --git a/modules/authority/contracts/api/local/grpc/contract.binpb b/modules/authority/contracts/api/local/grpc/contract.binpb index 9807338a..236b452b 100644 Binary files a/modules/authority/contracts/api/local/grpc/contract.binpb and b/modules/authority/contracts/api/local/grpc/contract.binpb differ diff --git a/modules/authority/contracts/api/local/grpc/proto/host/authority/v1/authority.proto b/modules/authority/contracts/api/local/grpc/proto/host/authority/v1/authority.proto index c9c9a99d..4df9e58a 100644 --- a/modules/authority/contracts/api/local/grpc/proto/host/authority/v1/authority.proto +++ b/modules/authority/contracts/api/local/grpc/proto/host/authority/v1/authority.proto @@ -274,6 +274,15 @@ message CheckRequest { // applies what the audience module's delivery declares for that method, and // refuses by name a call that does not hold it, a method that declares // nothing, and a method the module does not serve. + // Two exact methods are reserved for installation setup instead of delivered + // method policy: "POST /_codefly/installations" and + // "/host.installation.v1.InstallableService/Install". They accept only the + // setup-only credential for the exact current consented pending installation; + // ordinary capabilities are refused, even if delivered policy permits them. + // The receiver supplies its actual method and its own delivered audience, + // never values selected by the request body. The verified organization and + // installation seal must match the setup body and carriers before callback + // execution or a cached acknowledgement. Setup admits no ordinary data work. string method = 3; // Advisory asks for the policy decision without its refusal: the capability // is still verified and refused when it is not sound, but what the method @@ -281,6 +290,8 @@ message CheckRequest { // capability is answered. A hop in a composition that runs permissive // (hop/ENFORCEMENT=warn) asks this way and logs the refusal it did not make. // Hosts may refuse advisory outside local development runs. + // At either reserved setup method, advisory does not relax setup credential + // authentication or its pending-attempt checks: a refusal is always an error. bool advisory = 4; } diff --git a/modules/authority/contracts/api/local/grpc/proto/host/composition/v1/composition.proto b/modules/authority/contracts/api/local/grpc/proto/host/composition/v1/composition.proto index a834fd4c..f0f10078 100644 --- a/modules/authority/contracts/api/local/grpc/proto/host/composition/v1/composition.proto +++ b/modules/authority/contracts/api/local/grpc/proto/host/composition/v1/composition.proto @@ -67,6 +67,19 @@ message AuthorityManifestV1 { // delivery withdrawing it refuses the owner-selected capabilities issued // before. Every solution receiving its roles' scopes declares it. bool owner_aware = 7; + // Core-validated selector, carried verbatim: empty is legacy caller selection, + // "none" declares no standing machine authority, otherwise a declared role. + string installation_role = 8; + // Viewer-edge presence only; grants no authority. Nil and empty are equivalent. + repeated EdgeDependencyV1 edge_dependencies = 9; +} + +// EdgeDependencyV1 names the exact signed module principal. The host resolves +// it to a live catalogue target; it is not a target UUID, route alias or binding id. +message EdgeDependencyV1 { + string module = 1; + // Core validates required or optional; delivery does not default this value. + string presence = 2; } // MethodPolicyV1 is what a call to one method must hold, as the module's diff --git a/modules/authority/contracts/api/local/grpc/proto/host/installation/v1/installation.proto b/modules/authority/contracts/api/local/grpc/proto/host/installation/v1/installation.proto index f3f3f139..a2edbc62 100644 --- a/modules/authority/contracts/api/local/grpc/proto/host/installation/v1/installation.proto +++ b/modules/authority/contracts/api/local/grpc/proto/host/installation/v1/installation.proto @@ -264,6 +264,19 @@ message ReviewedOffer { // False when this run delivered no prepared Runnable bindings: no tool is // shown, which says nothing about what a composed binding would expose. bool tools_composed = 6; + // Signed machine-role selector: empty is undeclared, "none" is no standing + // authority, otherwise a named role. This is not a person or team grant. + string installation_role = 7; + // Exact declared viewer-edge presence, covered by the reviewed digest. + // Required access and per-module approval are separate host review concerns. + repeated OfferDependency edge_dependencies = 8; +} + +// OfferDependency carries presence only, never scopes or inferred binding ids. +// Module is the exact signed principal, resolved by the host to its live target. +message OfferDependency { + string module = 1; + string presence = 2; } // OfferBinding is one declared call from the solution to a module. diff --git a/modules/authority/module.package.codefly.yaml b/modules/authority/module.package.codefly.yaml index fb9fe2f3..4535268b 100644 --- a/modules/authority/module.package.codefly.yaml +++ b/modules/authority/module.package.codefly.yaml @@ -26,7 +26,7 @@ services: kind: protobuf package: "" path: contracts/api/local/grpc/contract.binpb - digest: sha256:2d74a1ad05ecab544606e2e52630cc5cb6c16729371f091262dfb45a686d2712 + digest: sha256:b49a5399e647c61b1991fc22bf7fcf8262ab17333dbe01b4afd1d16c346f3ea7 - endpoint: rest kind: openapi package: api.proto diff --git a/modules/authority/services/local/code/fixtures/local.yaml b/modules/authority/services/local/code/fixtures/local.yaml index 52290c91..9cc9b674 100644 --- a/modules/authority/services/local/code/fixtures/local.yaml +++ b/modules/authority/services/local/code/fixtures/local.yaml @@ -10,6 +10,12 @@ organizations: name: Acme - id: globex name: Globex +# The team directory. A team a person below belongs to exists already; list +# here a team nobody belongs to yet, which a role may still be granted to. +teams: + - {organization: acme, id: finance} + - {organization: acme, id: engineering} + - {organization: globex, id: ops} roles: - organization: acme name: viewer diff --git a/modules/authority/services/local/code/go.mod b/modules/authority/services/local/code/go.mod index 50cd9392..b004684d 100644 --- a/modules/authority/services/local/code/go.mod +++ b/modules/authority/services/local/code/go.mod @@ -5,7 +5,7 @@ go 1.27.3 require ( buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.12-20260825204119-511051f7f437.2 buf.build/go/protovalidate v1.4.0 - connectrpc.com/connect v1.21.0 + connectrpc.com/connect v1.21.1 connectrpc.com/vanguard v0.4.0 github.com/codefly-dev/authority/libraries/authority-client/go v0.0.0-20261026232801-7b6569e1f285 github.com/codefly-dev/authority/libraries/authority-core/go v0.0.0 @@ -14,15 +14,16 @@ require ( github.com/codefly-dev/authority/libraries/fixture-composition/go v0.0.0 github.com/codefly-dev/authority/libraries/hop/go v0.0.0 github.com/codefly-dev/authority/libraries/resources/go v0.0.0 - github.com/codefly-dev/core v0.19.7-0.20261026092055-b23dc13543b2 + github.com/codefly-dev/core v0.19.17-0.20261029043538-2aa2e9aaf950 github.com/codefly-dev/sdk-go v0.4.3 github.com/google/uuid v1.6.0 - github.com/grpc-ecosystem/grpc-gateway/v2 v2.31.0 + github.com/grpc-ecosystem/grpc-gateway/v2 v2.33.0 github.com/jackc/pgx/v5 v5.11.0 github.com/rs/cors v1.11.1 - golang.org/x/net v0.60.0 - google.golang.org/genproto/googleapis/api v0.0.0-20261019180522-ce908d29299b - google.golang.org/grpc v1.84.0 + go.yaml.in/yaml/v3 v3.0.5 + golang.org/x/net v0.62.0 + google.golang.org/genproto/googleapis/api v0.0.0-20261026165753-2ffab32bce7e + google.golang.org/grpc v1.85.0 google.golang.org/protobuf v1.36.12 gopkg.in/yaml.v3 v3.0.1 ) @@ -37,13 +38,13 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/go-logr/logr v1.4.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/analysis v1.0.0 // indirect + github.com/go-openapi/analysis v1.0.1 // indirect github.com/go-openapi/errors v0.22.9 // indirect - github.com/go-openapi/jsonpointer v1.0.1 // indirect - github.com/go-openapi/jsonreference v1.0.2 // indirect + github.com/go-openapi/jsonpointer v1.0.2 // indirect + github.com/go-openapi/jsonreference v1.0.3 // indirect github.com/go-openapi/loads v0.25.3 // indirect github.com/go-openapi/spec v1.0.1 // indirect - github.com/go-openapi/strfmt v0.27.2 // indirect + github.com/go-openapi/strfmt v0.27.4 // indirect github.com/go-openapi/swag/conv v0.29.2 // indirect github.com/go-openapi/swag/jsonutils v0.29.2 // indirect github.com/go-openapi/swag/loading v0.29.2 // indirect @@ -77,13 +78,12 @@ require ( go.opentelemetry.io/otel/sdk v1.47.0 // indirect go.opentelemetry.io/otel/trace v1.47.0 // indirect go.opentelemetry.io/proto/otlp v1.11.0 // indirect - go.yaml.in/yaml/v3 v3.0.5 // indirect - golang.org/x/exp v0.0.0-20260908205506-85c1c2202aba // indirect - golang.org/x/sync v0.23.0 // indirect + golang.org/x/exp v0.0.0-20261014143641-f0251eb4aa3e // indirect + golang.org/x/sync v0.24.0 // indirect golang.org/x/sys v0.49.0 // indirect - golang.org/x/term v0.46.0 // indirect - golang.org/x/text v0.42.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20261012220534-4341edf3f941 // indirect + golang.org/x/term v0.47.0 // indirect + golang.org/x/text v0.43.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20261026165753-2ffab32bce7e // indirect ) replace github.com/codefly-dev/authority/libraries/conformance/go => ../../../../../libraries/conformance/go diff --git a/modules/authority/services/local/code/go.sum b/modules/authority/services/local/code/go.sum index ea8e9d0e..c679884e 100644 --- a/modules/authority/services/local/code/go.sum +++ b/modules/authority/services/local/code/go.sum @@ -12,6 +12,8 @@ cel.dev/expr v0.25.3 h1:A2jO8jwOugrrovveCWfj0KEZOfqiLgAcwjpHPhzIGw0= cel.dev/expr v0.25.3/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= connectrpc.com/connect v1.21.0 h1:LhqSJt7jHf5NJBo9Jq/t/9FjcYAideif0mg+qe2jCUs= connectrpc.com/connect v1.21.0/go.mod h1:A2ygJrukXwWy32vkCAAHNVguZrqZ+jeZ9rGRnGR4dN4= +connectrpc.com/connect v1.21.1 h1:h2kacdlzXpZU2OQFSv98xkjYXNUSr/ptxmGVbS/+CRM= +connectrpc.com/connect v1.21.1/go.mod h1:fhMcVJ5w6r/yTYhnkEbW6+sIF7OZ5Ay7KYgmfebh2ek= connectrpc.com/vanguard v0.4.0 h1:lx23IDorlJnaR1mNbjgP0LXiI5yBwo0eWeXA5qSBNoY= connectrpc.com/vanguard v0.4.0/go.mod h1:VbDkW6OqfRPOi144sbE+OuLiLmhLfCxkQjzKErJsoT0= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= @@ -42,6 +44,8 @@ github.com/codefly-dev/authority/libraries/authority-client/go v0.0.0-2026102623 github.com/codefly-dev/authority/libraries/authority-client/go v0.0.0-20261026232801-7b6569e1f285/go.mod h1:4hRjiWuJOKzrEk8AS87pbWCbpiEmhEvQBGQaJo5Hmkg= github.com/codefly-dev/core v0.19.7-0.20261026092055-b23dc13543b2 h1:I1wL0ANXhTdQuEAnxXF+s4+rKILlBu/QHILq4Skyi3I= github.com/codefly-dev/core v0.19.7-0.20261026092055-b23dc13543b2/go.mod h1:1ina3kP+YSAL4tlZvnEYd4P8bFGnJECfkP86ojeeF1A= +github.com/codefly-dev/core v0.19.17-0.20261029043538-2aa2e9aaf950 h1:U5HeCDUfftwVFbMYN1HGPUL+9HO+ske3nkExu1C2kYY= +github.com/codefly-dev/core v0.19.17-0.20261029043538-2aa2e9aaf950/go.mod h1:Q7PHKw8SFb1wNxlTy2EgG11cI9Zmq55TYY1fdsQUhpE= github.com/codefly-dev/gortk v0.2.0 h1:7bOlS5valYz2zil+fZctQNcPCYBcPj86abcw9N8h1hQ= github.com/codefly-dev/gortk v0.2.0/go.mod h1:dDWUMFgAP063OCGhTEpV7FFUj9+zTcxxO/nV80VKEwg= github.com/codefly-dev/sdk-go v0.4.3 h1:f7QHqqE7xqqpglOQ2kJ+DZCO/TbrNaDEZhGZ1IOd46Q= @@ -80,18 +84,26 @@ github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-openapi/analysis v1.0.0 h1:sNvbAGCJqUTqIAodr9IVqJMmuZas3YS9ms1dGK9yiJ4= github.com/go-openapi/analysis v1.0.0/go.mod h1:NhYjJ57fnE+bcE7UwrJyMkhWA3Dfz7TdiBfTVAnos4Y= +github.com/go-openapi/analysis v1.0.1 h1:JTEyujgl4GyFj8pbMUY2yZx5ak1Ozt/DaqzQjQBnBWM= +github.com/go-openapi/analysis v1.0.1/go.mod h1:f3gYbXmnXSQIZB02DqE4f8JX969PdOEnaY7gDBLeJjI= github.com/go-openapi/errors v0.22.9 h1:HI9+SyVYiRzyeBQGv0CbWbQa+u2S0nny7G7AhY+UMiM= github.com/go-openapi/errors v0.22.9/go.mod h1:R73q66sXP2smzAGgFOI4nqtusRK2xN5pYCjsij+KX58= github.com/go-openapi/jsonpointer v1.0.1 h1:2KxywRmNwJkT/FMBa3iRNHEaAxSJvjqoufQZy3au1Mg= github.com/go-openapi/jsonpointer v1.0.1/go.mod h1:wI7ZYsFmbIi9nBXOZqgDaS/bqOchRGZjqxFli7FBYxY= +github.com/go-openapi/jsonpointer v1.0.2 h1:NbW7xT2b+l7x+JefiPGDsZWsrLk1ogrz4AHK1TTzMt8= +github.com/go-openapi/jsonpointer v1.0.2/go.mod h1:N0rI4MbNKD7xAFTiHtB+5Nhrg2j/wwYg7m7Fmb4/jk8= github.com/go-openapi/jsonreference v1.0.2 h1:oS4et8FOf3p3UQxEo4Xt0esijmBUM+F259Xl72OSZsc= github.com/go-openapi/jsonreference v1.0.2/go.mod h1:TbUNSOo+fcorZjFaNoiSDSoaNnnZtqJtLGR1PuvE/Cs= +github.com/go-openapi/jsonreference v1.0.3 h1:5rDbq+JsjCv6/nUDDJt83nLZoADI4w03uvHXer+4aYQ= +github.com/go-openapi/jsonreference v1.0.3/go.mod h1:OJSlMXHysgH272D9z2tiy2MGe5A9vQLHaCPb6KbYdE4= github.com/go-openapi/loads v0.25.3 h1:V+jKy/thXWdLJUuYC8sZX2dICyAa8M3DokF70jj34P0= github.com/go-openapi/loads v0.25.3/go.mod h1:LgLyCSOLBL2Qnj0Ps1oo2YH8jZoEKsMeVZCc+ALIxFo= github.com/go-openapi/spec v1.0.1 h1:lj2vdGpNDcVgwRc6qXdw6qt/KQpCtSa9tnUH6vpDPDk= github.com/go-openapi/spec v1.0.1/go.mod h1:M//GWQGtDUAjnP37gE6fInLgaczB+FatoipV3H1fYw8= github.com/go-openapi/strfmt v0.27.2 h1:SG32SlbwNy92s0KJiVxt2joJeFdqIYHvwrA0OU6HqzQ= github.com/go-openapi/strfmt v0.27.2/go.mod h1:M4CKsMO0Fb8qR10+1Ra75wCKNNquy+Vj+4LWZrhTo2E= +github.com/go-openapi/strfmt v0.27.4 h1:E1xsvHb8R9vsuYru4TP2PVVTv9AIgSFAUACUZrFQfCI= +github.com/go-openapi/strfmt v0.27.4/go.mod h1:SCOff81PPhU26acBiW96jHaFycEbT2nkQRvojM+V2z0= github.com/go-openapi/swag v0.29.2 h1:9n8frkcsuQRA0INU31VbxNxt1zBaHxrHYGEf5BUjFi4= github.com/go-openapi/swag v0.29.2/go.mod h1:RkCiX1gCVXWgLOavDXE3ALJrQ/VO2ziiIzOUs3z10xo= github.com/go-openapi/swag/cmdutils v0.29.2 h1:cXEzX/nWCODon251f+1HgL75/R4XXML5IZxgLCWMoro= @@ -141,6 +153,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.31.0 h1:Bd7KaOxzULLxtZ/K5s1aLbWhR0+5RToO65TXHsf3bqQ= github.com/grpc-ecosystem/grpc-gateway/v2 v2.31.0/go.mod h1:nN7ts3dFXKtCZWc//yfkpcQNKJABg16/uDVAZpLDalo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.33.0 h1:5T5Ns3kMpWr+z3s5ZA0e7ljVyWNVNxK76sHPAxi5D1k= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.33.0/go.mod h1:oWNYw09cH8wl8fFgC5hKlBLxR4XfKy2KQpP8MJjeCcc= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -247,26 +261,42 @@ go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/exp v0.0.0-20260908205506-85c1c2202aba h1:Ck8QetSgk912qxWLMCKxd0in+aiyBQyDSMae6e/xmpU= golang.org/x/exp v0.0.0-20260908205506-85c1c2202aba/go.mod h1:50RgIsmK7OwqzTTeqcSXQW8SswW0o8fRcDxmqGluJ8E= +golang.org/x/exp v0.0.0-20261014143641-f0251eb4aa3e h1:HgI6W1OolKK6tcV74is+rl9SMT0njsHlp0TbQiGXI/A= +golang.org/x/exp v0.0.0-20261014143641-f0251eb4aa3e/go.mod h1:DBoxwWhjaW8DJJw/YeM4zpeOKMC1FMXSblsYy+1mFSw= golang.org/x/mod v0.42.0 h1:ICeL3t2L30ljSPHHxDnR4fqVcBt/zR2zJAb9juavmQg= golang.org/x/mod v0.42.0/go.mod h1:u5fzIrRInkL8MaO1cbRkUHT64v303YIT5KEqpNr7jRs= golang.org/x/net v0.60.0 h1:79p50tfZlm0J9YfoDsSi639qSXNGVwEzOPLCxM2FsYU= golang.org/x/net v0.60.0/go.mod h1:2DA/G1UfVbCpQPeWTmMPGY7Cs2PkBkwu743bVX5PIVg= +golang.org/x/net v0.62.0 h1:18ELalfM5+XNRwzM2nYqXsZQ9ByS1uhj2O/U7+5jqRY= +golang.org/x/net v0.62.0/go.mod h1:WgDCOTH8iwtB66T4/GfI94AHo/hhdb7Hah9NDseQfiI= golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk= golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0= +golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA= +golang.org/x/sync v0.24.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0= golang.org/x/sys v0.49.0 h1:XbzkgYJHdqh/8m2Uu0W/dQv8nktxx4BFHp1M0gROTXA= golang.org/x/sys v0.49.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= golang.org/x/term v0.46.0 h1:3+OXuTbaKDgwk8jTi3aSLHRlmWqHEUDUtxnbFigO4YE= golang.org/x/term v0.46.0/go.mod h1:+K02xbkittuwc0Am4abfA3Fc+XRGXkvBXNO88NCXPoc= +golang.org/x/term v0.47.0 h1:cpyOwQkr2Nzxe2p6KnrTG7Ei2wtzjsuSjhpXXepZ/BM= +golang.org/x/term v0.47.0/go.mod h1:efAfAuG6RKABvg1ArGebRkvX6LXul14SM67o2e4mUCY= golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI= golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E= +golang.org/x/text v0.43.0 h1:1QivrlhwAsnMOcqgOdl9my7s9OyZl8O5/UGUTJVXoWc= +golang.org/x/text v0.43.0/go.mod h1:hbSIYA/amXcRXTFSZqh8tWHEoqRvvbKPJ2uz/KU+a+s= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/genproto/googleapis/api v0.0.0-20261019180522-ce908d29299b h1:nB3spRIaSPtxJpxb0k41yljROvm2Jrwfs2pSNdKS5y8= google.golang.org/genproto/googleapis/api v0.0.0-20261019180522-ce908d29299b/go.mod h1:9N4vtjEwvgQKEdlFVQO0M44ErhecQ9DCSQfGoZ51M6w= +google.golang.org/genproto/googleapis/api v0.0.0-20261026165753-2ffab32bce7e h1:e789sLZjeTdc02ZCQGTSzHOu0fR73+dwPQVKUp67Z88= +google.golang.org/genproto/googleapis/api v0.0.0-20261026165753-2ffab32bce7e/go.mod h1:vHV3wJM9WZyfV7GyB+OQGq5dWVrFwCDxapPK4WlRD88= google.golang.org/genproto/googleapis/rpc v0.0.0-20261012220534-4341edf3f941 h1:QG6hS4zFplRZvOQedzei4t2pT/zkQc3VR1u+S9/rcAw= google.golang.org/genproto/googleapis/rpc v0.0.0-20261012220534-4341edf3f941/go.mod h1:OaIUM3+LpYcK2GXM4FTmhWoIq371Owdr+Cc7/BsYHHc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20261026165753-2ffab32bce7e h1:w3fy/G8OrJHYexn+u5y7hFyc8YRRIr7DCevtIOwAcGY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20261026165753-2ffab32bce7e/go.mod h1:OaIUM3+LpYcK2GXM4FTmhWoIq371Owdr+Cc7/BsYHHc= google.golang.org/grpc v1.84.0 h1:soMyaPJ8pAak5PIQ0DGBUir0XRo2fRoMqhNWMLlLxO0= google.golang.org/grpc v1.84.0/go.mod h1:ljCht0DrxQrXBDRTZp52Qxh3Ffk8CdYm2sj4O2QN2C0= +google.golang.org/grpc v1.85.0 h1:BPBCfuVRQ1qvBhKDOq9uvQllMvO59rKsYhXpp7Dd8vw= +google.golang.org/grpc v1.85.0/go.mod h1:0rQrOD58yjal/0EaVbGBANindixsCZNQPDV3VKtaKuQ= google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc= google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/modules/authority/services/local/code/pkg/business/state/state.go b/modules/authority/services/local/code/pkg/business/state/state.go index fe317edb..c263513d 100644 --- a/modules/authority/services/local/code/pkg/business/state/state.go +++ b/modules/authority/services/local/code/pkg/business/state/state.go @@ -156,6 +156,10 @@ type fixtureFile struct { ID string `yaml:"id"` Name string `yaml:"name"` } `yaml:"organizations"` + Teams []struct { + ID string `yaml:"id"` + Organization string `yaml:"organization"` + } `yaml:"teams"` Roles []Role `yaml:"roles"` People []Person `yaml:"people"` Installations []FixtureInstallation `yaml:"installations"` @@ -266,6 +270,13 @@ func (s *State) LoadFixture(ctx context.Context, data []byte) error { s.orgs[o.ID] = o.Name s.store.PutOrganization(domain.Organization{ID: o.ID, Name: o.Name}) } + for _, team := range f.Teams { + if _, ok := s.orgs[team.Organization]; !ok || team.ID == "" { + s.mu.Unlock() + return fmt.Errorf("fixture: team %q names no id or an unknown organization %q", team.ID, team.Organization) + } + s.store.PutTeam(team.Organization, team.ID) + } for _, r := range f.Roles { if r.Generated && (r.Name != "developer" || len(r.Scopes) != 0) { s.mu.Unlock() @@ -294,6 +305,11 @@ func (s *State) LoadFixture(ctx context.Context, data []byte) error { s.people[p.ID] = p s.byToken[p.FixtureToken] = p.ID s.store.PutPerson(domain.Person{ID: p.ID, Organization: p.Organization, Roles: slices.Clone(p.Roles), Teams: slices.Clone(p.Teams)}) + // The fixture is this host's team directory: a team it seats a person + // in exists, and teams: lists the ones nobody belongs to yet. + for _, team := range p.Teams { + s.store.PutTeam(p.Organization, team) + } } s.refreshRolesLocked() s.mu.Unlock() diff --git a/modules/authority/services/local/code/pkg/business/state/team_fixture_test.go b/modules/authority/services/local/code/pkg/business/state/team_fixture_test.go new file mode 100644 index 00000000..610c55cd --- /dev/null +++ b/modules/authority/services/local/code/pkg/business/state/team_fixture_test.go @@ -0,0 +1,42 @@ +package state_test + +import ( + "context" + "strings" + "testing" + + "github.com/codefly-dev/authority/libraries/authority-core/go/authority" + + "gateway/pkg/business/state" + "gateway/pkg/business/testkit" +) + +// The fixture is the local host's team directory: a team it lists exists with +// nobody in it, and a team it seats a person in exists too. +func TestTheFixtureTeamDirectoryHoldsEmptyAndSeatedTeams(t *testing.T) { + k := testkit.New(t) + if err := k.State.LoadFixture(context.Background(), []byte(`teams: + - {organization: acme, id: reviewers} +people: + - {id: sam, fixture_token: fixture-sam, organization: acme, roles: [], teams: [seated]} +`)); err != nil { + t.Fatal(err) + } + inst := state.InstallationID("acme", "wiki") + for _, team := range []string{"reviewers", "seated", "finance"} { + if _, err := k.State.Service().GrantRoleAsHost(context.Background(), authority.RoleRequest{Organization: "acme", InstallationID: inst, Role: "viewer", TeamID: team}); err != nil { + t.Fatalf("team %s: %v", team, err) + } + } + for _, team := range []string{"ops", "nobody"} { + _, err := k.State.Service().GrantRoleAsHost(context.Background(), authority.RoleRequest{Organization: "acme", InstallationID: inst, Role: "viewer", TeamID: team}) + if err == nil || !strings.Contains(err.Error(), "not a team of") { + t.Fatalf("team %s: want a refusal naming not a team of, got %v", team, err) + } + } + for _, bad := range []string{"teams: [{organization: initech, id: x}]", "teams: [{organization: acme}]"} { + if err := testkit.New(t).State.LoadFixture(context.Background(), []byte(bad)); err == nil || !strings.Contains(err.Error(), "fixture: team") { + t.Fatalf("%s: want a fixture refusal, got %v", bad, err) + } + } +} diff --git a/modules/authority/services/local/code/pkg/gen/host/authority/v1/authority.pb.go b/modules/authority/services/local/code/pkg/gen/host/authority/v1/authority.pb.go index 1a3b3617..31cba370 100644 --- a/modules/authority/services/local/code/pkg/gen/host/authority/v1/authority.pb.go +++ b/modules/authority/services/local/code/pkg/gen/host/authority/v1/authority.pb.go @@ -827,6 +827,15 @@ type CheckRequest struct { // applies what the audience module's delivery declares for that method, and // refuses by name a call that does not hold it, a method that declares // nothing, and a method the module does not serve. + // Two exact methods are reserved for installation setup instead of delivered + // method policy: "POST /_codefly/installations" and + // "/host.installation.v1.InstallableService/Install". They accept only the + // setup-only credential for the exact current consented pending installation; + // ordinary capabilities are refused, even if delivered policy permits them. + // The receiver supplies its actual method and its own delivered audience, + // never values selected by the request body. The verified organization and + // installation seal must match the setup body and carriers before callback + // execution or a cached acknowledgement. Setup admits no ordinary data work. Method string `protobuf:"bytes,3,opt,name=method,proto3" json:"method,omitempty"` // Advisory asks for the policy decision without its refusal: the capability // is still verified and refused when it is not sound, but what the method @@ -834,6 +843,8 @@ type CheckRequest struct { // capability is answered. A hop in a composition that runs permissive // (hop/ENFORCEMENT=warn) asks this way and logs the refusal it did not make. // Hosts may refuse advisory outside local development runs. + // At either reserved setup method, advisory does not relax setup credential + // authentication or its pending-attempt checks: a refusal is always an error. Advisory bool `protobuf:"varint,4,opt,name=advisory,proto3" json:"advisory,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache diff --git a/modules/authority/services/local/code/pkg/gen/host/composition/v1/composition.pb.go b/modules/authority/services/local/code/pkg/gen/host/composition/v1/composition.pb.go index 808b88c3..92bcc01f 100644 --- a/modules/authority/services/local/code/pkg/gen/host/composition/v1/composition.pb.go +++ b/modules/authority/services/local/code/pkg/gen/host/composition/v1/composition.pb.go @@ -141,9 +141,14 @@ type AuthorityManifestV1 struct { // (Claims.AllowsOwned): only an audience declaring it receives one, and a // delivery withdrawing it refuses the owner-selected capabilities issued // before. Every solution receiving its roles' scopes declares it. - OwnerAware bool `protobuf:"varint,7,opt,name=owner_aware,json=ownerAware,proto3" json:"owner_aware,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + OwnerAware bool `protobuf:"varint,7,opt,name=owner_aware,json=ownerAware,proto3" json:"owner_aware,omitempty"` + // Core-validated selector, carried verbatim: empty is legacy caller selection, + // "none" declares no standing machine authority, otherwise a declared role. + InstallationRole string `protobuf:"bytes,8,opt,name=installation_role,json=installationRole,proto3" json:"installation_role,omitempty"` + // Viewer-edge presence only; grants no authority. Nil and empty are equivalent. + EdgeDependencies []*EdgeDependencyV1 `protobuf:"bytes,9,rep,name=edge_dependencies,json=edgeDependencies,proto3" json:"edge_dependencies,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *AuthorityManifestV1) Reset() { @@ -225,6 +230,75 @@ func (x *AuthorityManifestV1) GetOwnerAware() bool { return false } +func (x *AuthorityManifestV1) GetInstallationRole() string { + if x != nil { + return x.InstallationRole + } + return "" +} + +func (x *AuthorityManifestV1) GetEdgeDependencies() []*EdgeDependencyV1 { + if x != nil { + return x.EdgeDependencies + } + return nil +} + +// EdgeDependencyV1 names the exact signed module principal. The host resolves +// it to a live catalogue target; it is not a target UUID, route alias or binding id. +type EdgeDependencyV1 struct { + state protoimpl.MessageState `protogen:"open.v1"` + Module string `protobuf:"bytes,1,opt,name=module,proto3" json:"module,omitempty"` + // Core validates required or optional; delivery does not default this value. + Presence string `protobuf:"bytes,2,opt,name=presence,proto3" json:"presence,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *EdgeDependencyV1) Reset() { + *x = EdgeDependencyV1{} + mi := &file_host_composition_v1_composition_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *EdgeDependencyV1) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EdgeDependencyV1) ProtoMessage() {} + +func (x *EdgeDependencyV1) ProtoReflect() protoreflect.Message { + mi := &file_host_composition_v1_composition_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EdgeDependencyV1.ProtoReflect.Descriptor instead. +func (*EdgeDependencyV1) Descriptor() ([]byte, []int) { + return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{2} +} + +func (x *EdgeDependencyV1) GetModule() string { + if x != nil { + return x.Module + } + return "" +} + +func (x *EdgeDependencyV1) GetPresence() string { + if x != nil { + return x.Presence + } + return "" +} + // MethodPolicyV1 is what a call to one method must hold, as the module's // contract declares it with the authority.hop.v1.policy option. type MethodPolicyV1 struct { @@ -243,7 +317,7 @@ type MethodPolicyV1 struct { func (x *MethodPolicyV1) Reset() { *x = MethodPolicyV1{} - mi := &file_host_composition_v1_composition_proto_msgTypes[2] + mi := &file_host_composition_v1_composition_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -255,7 +329,7 @@ func (x *MethodPolicyV1) String() string { func (*MethodPolicyV1) ProtoMessage() {} func (x *MethodPolicyV1) ProtoReflect() protoreflect.Message { - mi := &file_host_composition_v1_composition_proto_msgTypes[2] + mi := &file_host_composition_v1_composition_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -268,7 +342,7 @@ func (x *MethodPolicyV1) ProtoReflect() protoreflect.Message { // Deprecated: Use MethodPolicyV1.ProtoReflect.Descriptor instead. func (*MethodPolicyV1) Descriptor() ([]byte, []int) { - return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{2} + return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{3} } func (x *MethodPolicyV1) GetMethod() string { @@ -304,7 +378,7 @@ type SignedManifestV1 struct { func (x *SignedManifestV1) Reset() { *x = SignedManifestV1{} - mi := &file_host_composition_v1_composition_proto_msgTypes[3] + mi := &file_host_composition_v1_composition_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -316,7 +390,7 @@ func (x *SignedManifestV1) String() string { func (*SignedManifestV1) ProtoMessage() {} func (x *SignedManifestV1) ProtoReflect() protoreflect.Message { - mi := &file_host_composition_v1_composition_proto_msgTypes[3] + mi := &file_host_composition_v1_composition_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -329,7 +403,7 @@ func (x *SignedManifestV1) ProtoReflect() protoreflect.Message { // Deprecated: Use SignedManifestV1.ProtoReflect.Descriptor instead. func (*SignedManifestV1) Descriptor() ([]byte, []int) { - return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{3} + return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{4} } func (x *SignedManifestV1) GetDocument() []byte { @@ -361,7 +435,7 @@ type ApplyManifestResponse struct { func (x *ApplyManifestResponse) Reset() { *x = ApplyManifestResponse{} - mi := &file_host_composition_v1_composition_proto_msgTypes[4] + mi := &file_host_composition_v1_composition_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -373,7 +447,7 @@ func (x *ApplyManifestResponse) String() string { func (*ApplyManifestResponse) ProtoMessage() {} func (x *ApplyManifestResponse) ProtoReflect() protoreflect.Message { - mi := &file_host_composition_v1_composition_proto_msgTypes[4] + mi := &file_host_composition_v1_composition_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -386,7 +460,7 @@ func (x *ApplyManifestResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ApplyManifestResponse.ProtoReflect.Descriptor instead. func (*ApplyManifestResponse) Descriptor() ([]byte, []int) { - return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{4} + return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{5} } func (x *ApplyManifestResponse) GetPrincipal() string { @@ -438,7 +512,7 @@ type ModuleManifestV1 struct { func (x *ModuleManifestV1) Reset() { *x = ModuleManifestV1{} - mi := &file_host_composition_v1_composition_proto_msgTypes[5] + mi := &file_host_composition_v1_composition_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -450,7 +524,7 @@ func (x *ModuleManifestV1) String() string { func (*ModuleManifestV1) ProtoMessage() {} func (x *ModuleManifestV1) ProtoReflect() protoreflect.Message { - mi := &file_host_composition_v1_composition_proto_msgTypes[5] + mi := &file_host_composition_v1_composition_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -463,7 +537,7 @@ func (x *ModuleManifestV1) ProtoReflect() protoreflect.Message { // Deprecated: Use ModuleManifestV1.ProtoReflect.Descriptor instead. func (*ModuleManifestV1) Descriptor() ([]byte, []int) { - return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{5} + return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{6} } func (x *ModuleManifestV1) GetModule() string { @@ -542,7 +616,7 @@ type RoleV1 struct { func (x *RoleV1) Reset() { *x = RoleV1{} - mi := &file_host_composition_v1_composition_proto_msgTypes[6] + mi := &file_host_composition_v1_composition_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -554,7 +628,7 @@ func (x *RoleV1) String() string { func (*RoleV1) ProtoMessage() {} func (x *RoleV1) ProtoReflect() protoreflect.Message { - mi := &file_host_composition_v1_composition_proto_msgTypes[6] + mi := &file_host_composition_v1_composition_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -567,7 +641,7 @@ func (x *RoleV1) ProtoReflect() protoreflect.Message { // Deprecated: Use RoleV1.ProtoReflect.Descriptor instead. func (*RoleV1) Descriptor() ([]byte, []int) { - return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{6} + return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{7} } func (x *RoleV1) GetName() string { @@ -613,7 +687,7 @@ type BindingV1 struct { func (x *BindingV1) Reset() { *x = BindingV1{} - mi := &file_host_composition_v1_composition_proto_msgTypes[7] + mi := &file_host_composition_v1_composition_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -625,7 +699,7 @@ func (x *BindingV1) String() string { func (*BindingV1) ProtoMessage() {} func (x *BindingV1) ProtoReflect() protoreflect.Message { - mi := &file_host_composition_v1_composition_proto_msgTypes[7] + mi := &file_host_composition_v1_composition_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -638,7 +712,7 @@ func (x *BindingV1) ProtoReflect() protoreflect.Message { // Deprecated: Use BindingV1.ProtoReflect.Descriptor instead. func (*BindingV1) Descriptor() ([]byte, []int) { - return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{7} + return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{8} } func (x *BindingV1) GetId() string { @@ -689,7 +763,7 @@ type ResourceKindV1 struct { func (x *ResourceKindV1) Reset() { *x = ResourceKindV1{} - mi := &file_host_composition_v1_composition_proto_msgTypes[8] + mi := &file_host_composition_v1_composition_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -701,7 +775,7 @@ func (x *ResourceKindV1) String() string { func (*ResourceKindV1) ProtoMessage() {} func (x *ResourceKindV1) ProtoReflect() protoreflect.Message { - mi := &file_host_composition_v1_composition_proto_msgTypes[8] + mi := &file_host_composition_v1_composition_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -714,7 +788,7 @@ func (x *ResourceKindV1) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceKindV1.ProtoReflect.Descriptor instead. func (*ResourceKindV1) Descriptor() ([]byte, []int) { - return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{8} + return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{9} } func (x *ResourceKindV1) GetKind() string { @@ -744,7 +818,7 @@ type PageV1 struct { func (x *PageV1) Reset() { *x = PageV1{} - mi := &file_host_composition_v1_composition_proto_msgTypes[9] + mi := &file_host_composition_v1_composition_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -756,7 +830,7 @@ func (x *PageV1) String() string { func (*PageV1) ProtoMessage() {} func (x *PageV1) ProtoReflect() protoreflect.Message { - mi := &file_host_composition_v1_composition_proto_msgTypes[9] + mi := &file_host_composition_v1_composition_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -769,7 +843,7 @@ func (x *PageV1) ProtoReflect() protoreflect.Message { // Deprecated: Use PageV1.ProtoReflect.Descriptor instead. func (*PageV1) Descriptor() ([]byte, []int) { - return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{9} + return file_host_composition_v1_composition_proto_rawDescGZIP(), []int{10} } func (x *PageV1) GetTitle() string { @@ -797,7 +871,7 @@ const file_host_composition_v1_composition_proto_rawDesc = "" + "\n" + "credential\x18\x03 \x01(\tR\n" + "credential\x12C\n" + - "\tauthority\x18\x04 \x01(\v2%.host.composition.v1.SignedManifestV1R\tauthority\"\xd9\x02\n" + + "\tauthority\x18\x04 \x01(\v2%.host.composition.v1.SignedManifestV1R\tauthority\"\xda\x03\n" + "\x13AuthorityManifestV1\x12\x16\n" + "\x06module\x18\x01 \x01(\tR\x06module\x12\x1e\n" + "\n" + @@ -808,7 +882,12 @@ const file_host_composition_v1_composition_proto_rawDesc = "" + "\x0escope_ceilings\x18\x05 \x03(\v2\x1e.host.authority.v1.WorkScopeV1R\rscopeCeilings\x121\n" + "\x05roles\x18\x06 \x03(\v2\x1b.host.composition.v1.RoleV1R\x05roles\x12\x1f\n" + "\vowner_aware\x18\a \x01(\bR\n" + - "ownerAware\"\x92\x01\n" + + "ownerAware\x12+\n" + + "\x11installation_role\x18\b \x01(\tR\x10installationRole\x12R\n" + + "\x11edge_dependencies\x18\t \x03(\v2%.host.composition.v1.EdgeDependencyV1R\x10edgeDependencies\"F\n" + + "\x10EdgeDependencyV1\x12\x16\n" + + "\x06module\x18\x01 \x01(\tR\x06module\x12\x1a\n" + + "\bpresence\x18\x02 \x01(\tR\bpresence\"\x92\x01\n" + "\x0eMethodPolicyV1\x12\x16\n" + "\x06method\x18\x01 \x01(\tR\x06method\x12:\n" + "\brequires\x18\x02 \x03(\v2\x1e.host.authority.v1.WorkScopeV1R\brequires\x12,\n" + @@ -864,44 +943,46 @@ func file_host_composition_v1_composition_proto_rawDescGZIP() []byte { return file_host_composition_v1_composition_proto_rawDescData } -var file_host_composition_v1_composition_proto_msgTypes = make([]protoimpl.MessageInfo, 10) +var file_host_composition_v1_composition_proto_msgTypes = make([]protoimpl.MessageInfo, 11) var file_host_composition_v1_composition_proto_goTypes = []any{ (*ApplyManifestRequest)(nil), // 0: host.composition.v1.ApplyManifestRequest (*AuthorityManifestV1)(nil), // 1: host.composition.v1.AuthorityManifestV1 - (*MethodPolicyV1)(nil), // 2: host.composition.v1.MethodPolicyV1 - (*SignedManifestV1)(nil), // 3: host.composition.v1.SignedManifestV1 - (*ApplyManifestResponse)(nil), // 4: host.composition.v1.ApplyManifestResponse - (*ModuleManifestV1)(nil), // 5: host.composition.v1.ModuleManifestV1 - (*RoleV1)(nil), // 6: host.composition.v1.RoleV1 - (*BindingV1)(nil), // 7: host.composition.v1.BindingV1 - (*ResourceKindV1)(nil), // 8: host.composition.v1.ResourceKindV1 - (*PageV1)(nil), // 9: host.composition.v1.PageV1 - (*v1.WorkScopeV1)(nil), // 10: host.authority.v1.WorkScopeV1 + (*EdgeDependencyV1)(nil), // 2: host.composition.v1.EdgeDependencyV1 + (*MethodPolicyV1)(nil), // 3: host.composition.v1.MethodPolicyV1 + (*SignedManifestV1)(nil), // 4: host.composition.v1.SignedManifestV1 + (*ApplyManifestResponse)(nil), // 5: host.composition.v1.ApplyManifestResponse + (*ModuleManifestV1)(nil), // 6: host.composition.v1.ModuleManifestV1 + (*RoleV1)(nil), // 7: host.composition.v1.RoleV1 + (*BindingV1)(nil), // 8: host.composition.v1.BindingV1 + (*ResourceKindV1)(nil), // 9: host.composition.v1.ResourceKindV1 + (*PageV1)(nil), // 10: host.composition.v1.PageV1 + (*v1.WorkScopeV1)(nil), // 11: host.authority.v1.WorkScopeV1 } var file_host_composition_v1_composition_proto_depIdxs = []int32{ - 5, // 0: host.composition.v1.ApplyManifestRequest.manifest:type_name -> host.composition.v1.ModuleManifestV1 - 3, // 1: host.composition.v1.ApplyManifestRequest.carrier:type_name -> host.composition.v1.SignedManifestV1 - 3, // 2: host.composition.v1.ApplyManifestRequest.authority:type_name -> host.composition.v1.SignedManifestV1 - 2, // 3: host.composition.v1.AuthorityManifestV1.methods:type_name -> host.composition.v1.MethodPolicyV1 - 10, // 4: host.composition.v1.AuthorityManifestV1.scope_ceilings:type_name -> host.authority.v1.WorkScopeV1 - 6, // 5: host.composition.v1.AuthorityManifestV1.roles:type_name -> host.composition.v1.RoleV1 - 10, // 6: host.composition.v1.MethodPolicyV1.requires:type_name -> host.authority.v1.WorkScopeV1 - 7, // 7: host.composition.v1.ModuleManifestV1.bindings:type_name -> host.composition.v1.BindingV1 - 8, // 8: host.composition.v1.ModuleManifestV1.resource_kinds:type_name -> host.composition.v1.ResourceKindV1 - 9, // 9: host.composition.v1.ModuleManifestV1.page:type_name -> host.composition.v1.PageV1 - 6, // 10: host.composition.v1.ModuleManifestV1.roles:type_name -> host.composition.v1.RoleV1 - 10, // 11: host.composition.v1.RoleV1.scopes:type_name -> host.authority.v1.WorkScopeV1 - 10, // 12: host.composition.v1.RoleV1.installation_owned_scopes:type_name -> host.authority.v1.WorkScopeV1 - 10, // 13: host.composition.v1.BindingV1.invoke_scopes:type_name -> host.authority.v1.WorkScopeV1 - 10, // 14: host.composition.v1.BindingV1.headless_scopes:type_name -> host.authority.v1.WorkScopeV1 - 10, // 15: host.composition.v1.BindingV1.lookup_scopes:type_name -> host.authority.v1.WorkScopeV1 - 0, // 16: host.composition.v1.CompositionService.ApplyManifest:input_type -> host.composition.v1.ApplyManifestRequest - 4, // 17: host.composition.v1.CompositionService.ApplyManifest:output_type -> host.composition.v1.ApplyManifestResponse - 17, // [17:18] is the sub-list for method output_type - 16, // [16:17] is the sub-list for method input_type - 16, // [16:16] is the sub-list for extension type_name - 16, // [16:16] is the sub-list for extension extendee - 0, // [0:16] is the sub-list for field type_name + 6, // 0: host.composition.v1.ApplyManifestRequest.manifest:type_name -> host.composition.v1.ModuleManifestV1 + 4, // 1: host.composition.v1.ApplyManifestRequest.carrier:type_name -> host.composition.v1.SignedManifestV1 + 4, // 2: host.composition.v1.ApplyManifestRequest.authority:type_name -> host.composition.v1.SignedManifestV1 + 3, // 3: host.composition.v1.AuthorityManifestV1.methods:type_name -> host.composition.v1.MethodPolicyV1 + 11, // 4: host.composition.v1.AuthorityManifestV1.scope_ceilings:type_name -> host.authority.v1.WorkScopeV1 + 7, // 5: host.composition.v1.AuthorityManifestV1.roles:type_name -> host.composition.v1.RoleV1 + 2, // 6: host.composition.v1.AuthorityManifestV1.edge_dependencies:type_name -> host.composition.v1.EdgeDependencyV1 + 11, // 7: host.composition.v1.MethodPolicyV1.requires:type_name -> host.authority.v1.WorkScopeV1 + 8, // 8: host.composition.v1.ModuleManifestV1.bindings:type_name -> host.composition.v1.BindingV1 + 9, // 9: host.composition.v1.ModuleManifestV1.resource_kinds:type_name -> host.composition.v1.ResourceKindV1 + 10, // 10: host.composition.v1.ModuleManifestV1.page:type_name -> host.composition.v1.PageV1 + 7, // 11: host.composition.v1.ModuleManifestV1.roles:type_name -> host.composition.v1.RoleV1 + 11, // 12: host.composition.v1.RoleV1.scopes:type_name -> host.authority.v1.WorkScopeV1 + 11, // 13: host.composition.v1.RoleV1.installation_owned_scopes:type_name -> host.authority.v1.WorkScopeV1 + 11, // 14: host.composition.v1.BindingV1.invoke_scopes:type_name -> host.authority.v1.WorkScopeV1 + 11, // 15: host.composition.v1.BindingV1.headless_scopes:type_name -> host.authority.v1.WorkScopeV1 + 11, // 16: host.composition.v1.BindingV1.lookup_scopes:type_name -> host.authority.v1.WorkScopeV1 + 0, // 17: host.composition.v1.CompositionService.ApplyManifest:input_type -> host.composition.v1.ApplyManifestRequest + 5, // 18: host.composition.v1.CompositionService.ApplyManifest:output_type -> host.composition.v1.ApplyManifestResponse + 18, // [18:19] is the sub-list for method output_type + 17, // [17:18] is the sub-list for method input_type + 17, // [17:17] is the sub-list for extension type_name + 17, // [17:17] is the sub-list for extension extendee + 0, // [0:17] is the sub-list for field type_name } func init() { file_host_composition_v1_composition_proto_init() } @@ -915,7 +996,7 @@ func file_host_composition_v1_composition_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_host_composition_v1_composition_proto_rawDesc), len(file_host_composition_v1_composition_proto_rawDesc)), NumEnums: 0, - NumMessages: 10, + NumMessages: 11, NumExtensions: 0, NumServices: 1, }, diff --git a/modules/authority/services/local/code/pkg/gen/host/installation/v1/installation.pb.go b/modules/authority/services/local/code/pkg/gen/host/installation/v1/installation.pb.go index 7498554d..c1513bb3 100644 --- a/modules/authority/services/local/code/pkg/gen/host/installation/v1/installation.pb.go +++ b/modules/authority/services/local/code/pkg/gen/host/installation/v1/installation.pb.go @@ -1223,8 +1223,14 @@ type ReviewedOffer struct { // False when this run delivered no prepared Runnable bindings: no tool is // shown, which says nothing about what a composed binding would expose. ToolsComposed bool `protobuf:"varint,6,opt,name=tools_composed,json=toolsComposed,proto3" json:"tools_composed,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // Signed machine-role selector: empty is undeclared, "none" is no standing + // authority, otherwise a named role. This is not a person or team grant. + InstallationRole string `protobuf:"bytes,7,opt,name=installation_role,json=installationRole,proto3" json:"installation_role,omitempty"` + // Exact declared viewer-edge presence, covered by the reviewed digest. + // Required access and per-module approval are separate host review concerns. + EdgeDependencies []*OfferDependency `protobuf:"bytes,8,rep,name=edge_dependencies,json=edgeDependencies,proto3" json:"edge_dependencies,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *ReviewedOffer) Reset() { @@ -1299,6 +1305,74 @@ func (x *ReviewedOffer) GetToolsComposed() bool { return false } +func (x *ReviewedOffer) GetInstallationRole() string { + if x != nil { + return x.InstallationRole + } + return "" +} + +func (x *ReviewedOffer) GetEdgeDependencies() []*OfferDependency { + if x != nil { + return x.EdgeDependencies + } + return nil +} + +// OfferDependency carries presence only, never scopes or inferred binding ids. +// Module is the exact signed principal, resolved by the host to its live target. +type OfferDependency struct { + state protoimpl.MessageState `protogen:"open.v1"` + Module string `protobuf:"bytes,1,opt,name=module,proto3" json:"module,omitempty"` + Presence string `protobuf:"bytes,2,opt,name=presence,proto3" json:"presence,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *OfferDependency) Reset() { + *x = OfferDependency{} + mi := &file_host_installation_v1_installation_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *OfferDependency) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OfferDependency) ProtoMessage() {} + +func (x *OfferDependency) ProtoReflect() protoreflect.Message { + mi := &file_host_installation_v1_installation_proto_msgTypes[19] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use OfferDependency.ProtoReflect.Descriptor instead. +func (*OfferDependency) Descriptor() ([]byte, []int) { + return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{19} +} + +func (x *OfferDependency) GetModule() string { + if x != nil { + return x.Module + } + return "" +} + +func (x *OfferDependency) GetPresence() string { + if x != nil { + return x.Presence + } + return "" +} + // OfferBinding is one declared call from the solution to a module. type OfferBinding struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -1312,7 +1386,7 @@ type OfferBinding struct { func (x *OfferBinding) Reset() { *x = OfferBinding{} - mi := &file_host_installation_v1_installation_proto_msgTypes[19] + mi := &file_host_installation_v1_installation_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1324,7 +1398,7 @@ func (x *OfferBinding) String() string { func (*OfferBinding) ProtoMessage() {} func (x *OfferBinding) ProtoReflect() protoreflect.Message { - mi := &file_host_installation_v1_installation_proto_msgTypes[19] + mi := &file_host_installation_v1_installation_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1337,7 +1411,7 @@ func (x *OfferBinding) ProtoReflect() protoreflect.Message { // Deprecated: Use OfferBinding.ProtoReflect.Descriptor instead. func (*OfferBinding) Descriptor() ([]byte, []int) { - return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{19} + return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{20} } func (x *OfferBinding) GetBinding() string { @@ -1375,7 +1449,7 @@ type OfferRole struct { func (x *OfferRole) Reset() { *x = OfferRole{} - mi := &file_host_installation_v1_installation_proto_msgTypes[20] + mi := &file_host_installation_v1_installation_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1387,7 +1461,7 @@ func (x *OfferRole) String() string { func (*OfferRole) ProtoMessage() {} func (x *OfferRole) ProtoReflect() protoreflect.Message { - mi := &file_host_installation_v1_installation_proto_msgTypes[20] + mi := &file_host_installation_v1_installation_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1400,7 +1474,7 @@ func (x *OfferRole) ProtoReflect() protoreflect.Message { // Deprecated: Use OfferRole.ProtoReflect.Descriptor instead. func (*OfferRole) Descriptor() ([]byte, []int) { - return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{20} + return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{21} } func (x *OfferRole) GetName() string { @@ -1440,7 +1514,7 @@ type OfferScope struct { func (x *OfferScope) Reset() { *x = OfferScope{} - mi := &file_host_installation_v1_installation_proto_msgTypes[21] + mi := &file_host_installation_v1_installation_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1452,7 +1526,7 @@ func (x *OfferScope) String() string { func (*OfferScope) ProtoMessage() {} func (x *OfferScope) ProtoReflect() protoreflect.Message { - mi := &file_host_installation_v1_installation_proto_msgTypes[21] + mi := &file_host_installation_v1_installation_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1465,7 +1539,7 @@ func (x *OfferScope) ProtoReflect() protoreflect.Message { // Deprecated: Use OfferScope.ProtoReflect.Descriptor instead. func (*OfferScope) Descriptor() ([]byte, []int) { - return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{21} + return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{22} } func (x *OfferScope) GetResourceKind() string { @@ -1513,7 +1587,7 @@ type OfferCapability struct { func (x *OfferCapability) Reset() { *x = OfferCapability{} - mi := &file_host_installation_v1_installation_proto_msgTypes[22] + mi := &file_host_installation_v1_installation_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1525,7 +1599,7 @@ func (x *OfferCapability) String() string { func (*OfferCapability) ProtoMessage() {} func (x *OfferCapability) ProtoReflect() protoreflect.Message { - mi := &file_host_installation_v1_installation_proto_msgTypes[22] + mi := &file_host_installation_v1_installation_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1538,7 +1612,7 @@ func (x *OfferCapability) ProtoReflect() protoreflect.Message { // Deprecated: Use OfferCapability.ProtoReflect.Descriptor instead. func (*OfferCapability) Descriptor() ([]byte, []int) { - return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{22} + return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{23} } func (x *OfferCapability) GetModule() string { @@ -1584,7 +1658,7 @@ type OfferTool struct { func (x *OfferTool) Reset() { *x = OfferTool{} - mi := &file_host_installation_v1_installation_proto_msgTypes[23] + mi := &file_host_installation_v1_installation_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1596,7 +1670,7 @@ func (x *OfferTool) String() string { func (*OfferTool) ProtoMessage() {} func (x *OfferTool) ProtoReflect() protoreflect.Message { - mi := &file_host_installation_v1_installation_proto_msgTypes[23] + mi := &file_host_installation_v1_installation_proto_msgTypes[24] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1609,7 +1683,7 @@ func (x *OfferTool) ProtoReflect() protoreflect.Message { // Deprecated: Use OfferTool.ProtoReflect.Descriptor instead. func (*OfferTool) Descriptor() ([]byte, []int) { - return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{23} + return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{24} } func (x *OfferTool) GetName() string { @@ -1645,7 +1719,7 @@ type ListHeldRequest struct { func (x *ListHeldRequest) Reset() { *x = ListHeldRequest{} - mi := &file_host_installation_v1_installation_proto_msgTypes[24] + mi := &file_host_installation_v1_installation_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1657,7 +1731,7 @@ func (x *ListHeldRequest) String() string { func (*ListHeldRequest) ProtoMessage() {} func (x *ListHeldRequest) ProtoReflect() protoreflect.Message { - mi := &file_host_installation_v1_installation_proto_msgTypes[24] + mi := &file_host_installation_v1_installation_proto_msgTypes[25] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1670,7 +1744,7 @@ func (x *ListHeldRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListHeldRequest.ProtoReflect.Descriptor instead. func (*ListHeldRequest) Descriptor() ([]byte, []int) { - return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{24} + return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{25} } func (x *ListHeldRequest) GetOrganizationId() string { @@ -1692,7 +1766,7 @@ type ListHeldResponse struct { func (x *ListHeldResponse) Reset() { *x = ListHeldResponse{} - mi := &file_host_installation_v1_installation_proto_msgTypes[25] + mi := &file_host_installation_v1_installation_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1704,7 +1778,7 @@ func (x *ListHeldResponse) String() string { func (*ListHeldResponse) ProtoMessage() {} func (x *ListHeldResponse) ProtoReflect() protoreflect.Message { - mi := &file_host_installation_v1_installation_proto_msgTypes[25] + mi := &file_host_installation_v1_installation_proto_msgTypes[26] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1717,7 +1791,7 @@ func (x *ListHeldResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListHeldResponse.ProtoReflect.Descriptor instead. func (*ListHeldResponse) Descriptor() ([]byte, []int) { - return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{25} + return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{26} } func (x *ListHeldResponse) GetInstallations() []*HeldInstallation { @@ -1740,7 +1814,7 @@ type HeldInstallation struct { func (x *HeldInstallation) Reset() { *x = HeldInstallation{} - mi := &file_host_installation_v1_installation_proto_msgTypes[26] + mi := &file_host_installation_v1_installation_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1752,7 +1826,7 @@ func (x *HeldInstallation) String() string { func (*HeldInstallation) ProtoMessage() {} func (x *HeldInstallation) ProtoReflect() protoreflect.Message { - mi := &file_host_installation_v1_installation_proto_msgTypes[26] + mi := &file_host_installation_v1_installation_proto_msgTypes[27] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1765,7 +1839,7 @@ func (x *HeldInstallation) ProtoReflect() protoreflect.Message { // Deprecated: Use HeldInstallation.ProtoReflect.Descriptor instead. func (*HeldInstallation) Descriptor() ([]byte, []int) { - return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{26} + return file_host_installation_v1_installation_proto_rawDescGZIP(), []int{27} } func (x *HeldInstallation) GetInstallation() *Installation { @@ -1865,14 +1939,19 @@ const file_host_installation_v1_installation_proto_rawDesc = "" + "\x06target\x18\x01 \x01(\tR\x06target\x12\x1e\n" + "\n" + "generation\x18\x02 \x01(\x04R\n" + - "generation\"\xce\x02\n" + + "generation\"\xcf\x03\n" + "\rReviewedOffer\x12A\n" + "\bapproval\x18\x01 \x01(\v2%.host.installation.v1.ConsentApprovalR\bapproval\x12\x16\n" + "\x06target\x18\x02 \x01(\tR\x06target\x125\n" + "\x05roles\x18\x03 \x03(\v2\x1f.host.installation.v1.OfferRoleR\x05roles\x12F\n" + "\finstallation\x18\x04 \x01(\v2\".host.installation.v1.InstallationR\finstallation\x12<\n" + "\aceiling\x18\x05 \x03(\v2\".host.installation.v1.OfferBindingR\aceiling\x12%\n" + - "\x0etools_composed\x18\x06 \x01(\bR\rtoolsComposed\"z\n" + + "\x0etools_composed\x18\x06 \x01(\bR\rtoolsComposed\x12+\n" + + "\x11installation_role\x18\a \x01(\tR\x10installationRole\x12R\n" + + "\x11edge_dependencies\x18\b \x03(\v2%.host.installation.v1.OfferDependencyR\x10edgeDependencies\"E\n" + + "\x0fOfferDependency\x12\x16\n" + + "\x06module\x18\x01 \x01(\tR\x06module\x12\x1a\n" + + "\bpresence\x18\x02 \x01(\tR\bpresence\"z\n" + "\fOfferBinding\x12\x18\n" + "\abinding\x18\x01 \x01(\tR\abinding\x12\x16\n" + "\x06module\x18\x02 \x01(\tR\x06module\x128\n" + @@ -1933,7 +2012,7 @@ func file_host_installation_v1_installation_proto_rawDescGZIP() []byte { } var file_host_installation_v1_installation_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_host_installation_v1_installation_proto_msgTypes = make([]protoimpl.MessageInfo, 27) +var file_host_installation_v1_installation_proto_msgTypes = make([]protoimpl.MessageInfo, 28) var file_host_installation_v1_installation_proto_goTypes = []any{ (Installation_Status)(0), // 0: host.installation.v1.Installation.Status (*CurrentInstallationRequest)(nil), // 1: host.installation.v1.CurrentInstallationRequest @@ -1955,14 +2034,15 @@ var file_host_installation_v1_installation_proto_goTypes = []any{ (*ListOffersResponse)(nil), // 17: host.installation.v1.ListOffersResponse (*Offer)(nil), // 18: host.installation.v1.Offer (*ReviewedOffer)(nil), // 19: host.installation.v1.ReviewedOffer - (*OfferBinding)(nil), // 20: host.installation.v1.OfferBinding - (*OfferRole)(nil), // 21: host.installation.v1.OfferRole - (*OfferScope)(nil), // 22: host.installation.v1.OfferScope - (*OfferCapability)(nil), // 23: host.installation.v1.OfferCapability - (*OfferTool)(nil), // 24: host.installation.v1.OfferTool - (*ListHeldRequest)(nil), // 25: host.installation.v1.ListHeldRequest - (*ListHeldResponse)(nil), // 26: host.installation.v1.ListHeldResponse - (*HeldInstallation)(nil), // 27: host.installation.v1.HeldInstallation + (*OfferDependency)(nil), // 20: host.installation.v1.OfferDependency + (*OfferBinding)(nil), // 21: host.installation.v1.OfferBinding + (*OfferRole)(nil), // 22: host.installation.v1.OfferRole + (*OfferScope)(nil), // 23: host.installation.v1.OfferScope + (*OfferCapability)(nil), // 24: host.installation.v1.OfferCapability + (*OfferTool)(nil), // 25: host.installation.v1.OfferTool + (*ListHeldRequest)(nil), // 26: host.installation.v1.ListHeldRequest + (*ListHeldResponse)(nil), // 27: host.installation.v1.ListHeldResponse + (*HeldInstallation)(nil), // 28: host.installation.v1.HeldInstallation } var file_host_installation_v1_installation_proto_depIdxs = []int32{ 5, // 0: host.installation.v1.InstallRequest.approval:type_name -> host.installation.v1.ConsentApproval @@ -1972,43 +2052,44 @@ var file_host_installation_v1_installation_proto_depIdxs = []int32{ 13, // 4: host.installation.v1.InstallationNotice.installation:type_name -> host.installation.v1.Installation 18, // 5: host.installation.v1.ListOffersResponse.offers:type_name -> host.installation.v1.Offer 5, // 6: host.installation.v1.ReviewedOffer.approval:type_name -> host.installation.v1.ConsentApproval - 21, // 7: host.installation.v1.ReviewedOffer.roles:type_name -> host.installation.v1.OfferRole + 22, // 7: host.installation.v1.ReviewedOffer.roles:type_name -> host.installation.v1.OfferRole 13, // 8: host.installation.v1.ReviewedOffer.installation:type_name -> host.installation.v1.Installation - 20, // 9: host.installation.v1.ReviewedOffer.ceiling:type_name -> host.installation.v1.OfferBinding - 22, // 10: host.installation.v1.OfferBinding.scopes:type_name -> host.installation.v1.OfferScope - 22, // 11: host.installation.v1.OfferRole.scopes:type_name -> host.installation.v1.OfferScope - 23, // 12: host.installation.v1.OfferRole.capabilities:type_name -> host.installation.v1.OfferCapability - 22, // 13: host.installation.v1.OfferCapability.requires:type_name -> host.installation.v1.OfferScope - 24, // 14: host.installation.v1.OfferCapability.tools:type_name -> host.installation.v1.OfferTool - 27, // 15: host.installation.v1.ListHeldResponse.installations:type_name -> host.installation.v1.HeldInstallation - 13, // 16: host.installation.v1.HeldInstallation.installation:type_name -> host.installation.v1.Installation - 1, // 17: host.installation.v1.InstallationService.Current:input_type -> host.installation.v1.CurrentInstallationRequest - 3, // 18: host.installation.v1.InstallationService.Install:input_type -> host.installation.v1.InstallRequest - 4, // 19: host.installation.v1.InstallationService.Review:input_type -> host.installation.v1.ReviewRequest - 6, // 20: host.installation.v1.InstallationService.GrantRole:input_type -> host.installation.v1.GrantRoleRequest - 7, // 21: host.installation.v1.InstallationService.RevokeRole:input_type -> host.installation.v1.RevokeRoleRequest - 8, // 22: host.installation.v1.InstallationService.ListRoleGrants:input_type -> host.installation.v1.ListRoleGrantsRequest - 11, // 23: host.installation.v1.InstallationService.List:input_type -> host.installation.v1.ListRequest - 25, // 24: host.installation.v1.InstallationService.ListHeld:input_type -> host.installation.v1.ListHeldRequest - 14, // 25: host.installation.v1.InstallableService.Install:input_type -> host.installation.v1.InstallationNotice - 16, // 26: host.installation.v1.OfferService.ListOffers:input_type -> host.installation.v1.ListOffersRequest - 4, // 27: host.installation.v1.OfferService.ReviewOffer:input_type -> host.installation.v1.ReviewRequest - 2, // 28: host.installation.v1.InstallationService.Current:output_type -> host.installation.v1.CurrentInstallation - 13, // 29: host.installation.v1.InstallationService.Install:output_type -> host.installation.v1.Installation - 5, // 30: host.installation.v1.InstallationService.Review:output_type -> host.installation.v1.ConsentApproval - 13, // 31: host.installation.v1.InstallationService.GrantRole:output_type -> host.installation.v1.Installation - 13, // 32: host.installation.v1.InstallationService.RevokeRole:output_type -> host.installation.v1.Installation - 9, // 33: host.installation.v1.InstallationService.ListRoleGrants:output_type -> host.installation.v1.ListRoleGrantsResponse - 12, // 34: host.installation.v1.InstallationService.List:output_type -> host.installation.v1.ListResponse - 26, // 35: host.installation.v1.InstallationService.ListHeld:output_type -> host.installation.v1.ListHeldResponse - 15, // 36: host.installation.v1.InstallableService.Install:output_type -> host.installation.v1.InstallationAck - 17, // 37: host.installation.v1.OfferService.ListOffers:output_type -> host.installation.v1.ListOffersResponse - 19, // 38: host.installation.v1.OfferService.ReviewOffer:output_type -> host.installation.v1.ReviewedOffer - 28, // [28:39] is the sub-list for method output_type - 17, // [17:28] is the sub-list for method input_type - 17, // [17:17] is the sub-list for extension type_name - 17, // [17:17] is the sub-list for extension extendee - 0, // [0:17] is the sub-list for field type_name + 21, // 9: host.installation.v1.ReviewedOffer.ceiling:type_name -> host.installation.v1.OfferBinding + 20, // 10: host.installation.v1.ReviewedOffer.edge_dependencies:type_name -> host.installation.v1.OfferDependency + 23, // 11: host.installation.v1.OfferBinding.scopes:type_name -> host.installation.v1.OfferScope + 23, // 12: host.installation.v1.OfferRole.scopes:type_name -> host.installation.v1.OfferScope + 24, // 13: host.installation.v1.OfferRole.capabilities:type_name -> host.installation.v1.OfferCapability + 23, // 14: host.installation.v1.OfferCapability.requires:type_name -> host.installation.v1.OfferScope + 25, // 15: host.installation.v1.OfferCapability.tools:type_name -> host.installation.v1.OfferTool + 28, // 16: host.installation.v1.ListHeldResponse.installations:type_name -> host.installation.v1.HeldInstallation + 13, // 17: host.installation.v1.HeldInstallation.installation:type_name -> host.installation.v1.Installation + 1, // 18: host.installation.v1.InstallationService.Current:input_type -> host.installation.v1.CurrentInstallationRequest + 3, // 19: host.installation.v1.InstallationService.Install:input_type -> host.installation.v1.InstallRequest + 4, // 20: host.installation.v1.InstallationService.Review:input_type -> host.installation.v1.ReviewRequest + 6, // 21: host.installation.v1.InstallationService.GrantRole:input_type -> host.installation.v1.GrantRoleRequest + 7, // 22: host.installation.v1.InstallationService.RevokeRole:input_type -> host.installation.v1.RevokeRoleRequest + 8, // 23: host.installation.v1.InstallationService.ListRoleGrants:input_type -> host.installation.v1.ListRoleGrantsRequest + 11, // 24: host.installation.v1.InstallationService.List:input_type -> host.installation.v1.ListRequest + 26, // 25: host.installation.v1.InstallationService.ListHeld:input_type -> host.installation.v1.ListHeldRequest + 14, // 26: host.installation.v1.InstallableService.Install:input_type -> host.installation.v1.InstallationNotice + 16, // 27: host.installation.v1.OfferService.ListOffers:input_type -> host.installation.v1.ListOffersRequest + 4, // 28: host.installation.v1.OfferService.ReviewOffer:input_type -> host.installation.v1.ReviewRequest + 2, // 29: host.installation.v1.InstallationService.Current:output_type -> host.installation.v1.CurrentInstallation + 13, // 30: host.installation.v1.InstallationService.Install:output_type -> host.installation.v1.Installation + 5, // 31: host.installation.v1.InstallationService.Review:output_type -> host.installation.v1.ConsentApproval + 13, // 32: host.installation.v1.InstallationService.GrantRole:output_type -> host.installation.v1.Installation + 13, // 33: host.installation.v1.InstallationService.RevokeRole:output_type -> host.installation.v1.Installation + 9, // 34: host.installation.v1.InstallationService.ListRoleGrants:output_type -> host.installation.v1.ListRoleGrantsResponse + 12, // 35: host.installation.v1.InstallationService.List:output_type -> host.installation.v1.ListResponse + 27, // 36: host.installation.v1.InstallationService.ListHeld:output_type -> host.installation.v1.ListHeldResponse + 15, // 37: host.installation.v1.InstallableService.Install:output_type -> host.installation.v1.InstallationAck + 17, // 38: host.installation.v1.OfferService.ListOffers:output_type -> host.installation.v1.ListOffersResponse + 19, // 39: host.installation.v1.OfferService.ReviewOffer:output_type -> host.installation.v1.ReviewedOffer + 29, // [29:40] is the sub-list for method output_type + 18, // [18:29] is the sub-list for method input_type + 18, // [18:18] is the sub-list for extension type_name + 18, // [18:18] is the sub-list for extension extendee + 0, // [0:18] is the sub-list for field type_name } func init() { file_host_installation_v1_installation_proto_init() } @@ -2022,7 +2103,7 @@ func file_host_installation_v1_installation_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_host_installation_v1_installation_proto_rawDesc), len(file_host_installation_v1_installation_proto_rawDesc)), NumEnums: 1, - NumMessages: 27, + NumMessages: 28, NumExtensions: 0, NumServices: 3, }, diff --git a/modules/authority/services/local/code/plugins/composition/installation_declaration_test.go b/modules/authority/services/local/code/plugins/composition/installation_declaration_test.go new file mode 100644 index 00000000..8a2f578f --- /dev/null +++ b/modules/authority/services/local/code/plugins/composition/installation_declaration_test.go @@ -0,0 +1,79 @@ +package composition + +import ( + "context" + "strings" + "testing" + + "gateway/pkg/business/state" + authorityv1 "gateway/pkg/gen/host/authority/v1" + compositionv1 "gateway/pkg/gen/host/composition/v1" + modulecontract "github.com/codefly-dev/core/contracts/module" + "github.com/codefly-dev/core/solutionhost" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// Uses generated messages, both real signatures and actual ApplyManifest +// persistence. CLI producer construction is qualified by the CLI owner. +func TestSignedInstallationDeclarationsReachStoredModule(t *testing.T) { + for _, selector := range []string{"", "none", "editor"} { + t.Run(selector, func(t *testing.T) { + p, k := verifying(t) + req := carrier(t, k, "wiki", solutionhost.KindSolution, 2, []string{"wiki.page"}, installationDocument(selector), installationManifest(selector)) + if _, err := p.ApplyManifest(context.Background(), req); err != nil { + t.Fatal(err) + } + got, ok := k.State.Module("wiki") + if !ok || got.InstallationRole != selector || len(got.EdgeDependencies) != 2 || got.EdgeDependencies[0].Module != "z-principal" || got.EdgeDependencies[1].Module != "a-principal" || got.EdgeDependencies[0].Presence != "required" || got.EdgeDependencies[1].Presence != "optional" { + t.Fatalf("stored declaration: %+v", got) + } + }) + } +} + +func installationDocument(selector string) *compositionv1.AuthorityManifestV1 { + page := scope("wiki.page", "read", "write") + return &compositionv1.AuthorityManifestV1{InstallationRole: selector, EdgeDependencies: []*compositionv1.EdgeDependencyV1{{Module: "z-principal", Presence: "required"}, {Module: "a-principal", Presence: "optional"}}, ScopeCeilings: []*authorityv1.WorkScopeV1{page}, Roles: []*compositionv1.RoleV1{{Name: state.InstallerRole, Scopes: []*authorityv1.WorkScopeV1{page}}, {Name: "editor", Scopes: []*authorityv1.WorkScopeV1{page}}}} +} + +func installationManifest(selector string) func(*solutionhost.ModuleManifest) { + return func(m *solutionhost.ModuleManifest) { + m.Authority.InstallationRole = selector + m.Authority.EdgeDependencies = []modulecontract.EdgeDependency{{Module: "z-principal", Presence: "required"}, {Module: "a-principal", Presence: "optional"}} + } +} + +func TestSignedInstallationDeclarationDisagreementRefusesBeforeStore(t *testing.T) { + for _, tc := range []struct { + name, field string + change func(**compositionv1.AuthorityManifestV1) + }{ + {"selector", "installation_role", func(d **compositionv1.AuthorityManifestV1) { (*d).InstallationRole = "editor" }}, + {"selector omitted", "installation_role", func(d **compositionv1.AuthorityManifestV1) { (*d).InstallationRole = "" }}, + {"sidecar absent", "installation_role", func(d **compositionv1.AuthorityManifestV1) { *d = nil }}, + {"edge identity", "edge_dependencies", func(d **compositionv1.AuthorityManifestV1) { (*d).EdgeDependencies[0].Module = "route-alias" }}, + {"edge presence", "edge_dependencies", func(d **compositionv1.AuthorityManifestV1) { (*d).EdgeDependencies[0].Presence = "optional" }}, + {"edge order", "edge_dependencies", func(d **compositionv1.AuthorityManifestV1) { + (*d).EdgeDependencies[0], (*d).EdgeDependencies[1] = (*d).EdgeDependencies[1], (*d).EdgeDependencies[0] + }}, + {"edges omitted", "edge_dependencies", func(d **compositionv1.AuthorityManifestV1) { (*d).EdgeDependencies = nil }}, + } { + t.Run(tc.name, func(t *testing.T) { + p, k := verifying(t) + before, _ := k.State.Module("wiki") + doc := installationDocument("none") + tc.change(&doc) + // The mismatching sidecar is correctly signed and pins the carrier. + req := carrier(t, k, "wiki", solutionhost.KindSolution, 2, []string{"wiki.page"}, doc, installationManifest("none")) + _, err := p.ApplyManifest(context.Background(), req) + if status.Code(err) != codes.PermissionDenied || !strings.Contains(err.Error(), tc.field) { + t.Fatalf("mismatch: %v", err) + } + after, _ := k.State.Module("wiki") + if after.Generation != before.Generation { + t.Fatal("refused declaration persisted") + } + }) + } +} diff --git a/modules/authority/services/local/code/plugins/composition/plugin.go b/modules/authority/services/local/code/plugins/composition/plugin.go index 1824eec0..8cd59c98 100644 --- a/modules/authority/services/local/code/plugins/composition/plugin.go +++ b/modules/authority/services/local/code/plugins/composition/plugin.go @@ -138,7 +138,10 @@ func (p *Plugin) document(signed, carrier *compositionv1.SignedManifestV1, core // DocumentOf reads this host's binding of the authority's document as the core's. func DocumentOf(in *compositionv1.AuthorityManifestV1) *delivered.Document { doc := &delivered.Document{Module: in.GetModule(), Generation: in.GetGeneration(), CoreManifestDigest: in.GetCoreManifestDigest(), - ScopeCeilings: issuer.FromWire(in.GetScopeCeilings()), OwnerAware: in.GetOwnerAware()} + ScopeCeilings: issuer.FromWire(in.GetScopeCeilings()), OwnerAware: in.GetOwnerAware(), InstallationRole: in.GetInstallationRole()} + for _, d := range in.GetEdgeDependencies() { + doc.EdgeDependencies = append(doc.EdgeDependencies, domain.EdgeDependency{Module: d.GetModule(), Presence: d.GetPresence()}) + } for _, m := range in.GetMethods() { doc.Methods = append(doc.Methods, delivered.Method{Method: m.GetMethod(), Requires: issuer.FromWire(m.GetRequires()), AnyCheckedCaller: m.GetAnyCheckedCaller()}) } diff --git a/modules/authority/services/local/code/plugins/installation/declaration_wire_test.go b/modules/authority/services/local/code/plugins/installation/declaration_wire_test.go new file mode 100644 index 00000000..fbeae813 --- /dev/null +++ b/modules/authority/services/local/code/plugins/installation/declaration_wire_test.go @@ -0,0 +1,33 @@ +package installation + +import ( + installationv1 "gateway/pkg/gen/host/installation/v1" + "github.com/codefly-dev/authority/libraries/authority-core/go/authority" + "github.com/codefly-dev/authority/libraries/authority-core/go/domain" + "google.golang.org/protobuf/proto" + "testing" +) + +func TestReviewedOfferWireCarriesExactInstallationDeclaration(t *testing.T) { + for _, selector := range []string{"", "none", "editor"} { + m := domain.Module{Name: "solution", InstallationRole: selector, EdgeDependencies: []domain.EdgeDependency{{Module: "z-principal", Presence: "required"}, {Module: "a-principal", Presence: "optional"}}} + wire, err := (&Plugin{}).review(authority.ReviewedOffer{Module: m}, nil) + if err != nil { + t.Fatal(err) + } + raw, err := proto.Marshal(wire) + if err != nil { + t.Fatal(err) + } + var got installationv1.ReviewedOffer + if err := proto.Unmarshal(raw, &got); err != nil { + t.Fatal(err) + } + if got.GetInstallationRole() != selector || len(got.GetEdgeDependencies()) != 2 || got.EdgeDependencies[0].GetModule() != "z-principal" || got.EdgeDependencies[1].GetModule() != "a-principal" || got.EdgeDependencies[0].GetPresence() != "required" || got.EdgeDependencies[1].GetPresence() != "optional" { + t.Fatalf("wire changed declaration: %v", &got) + } + if len(got.GetCeiling()) != 0 || len(got.GetRoles()) != 0 { + t.Fatal("presence created authority") + } + } +} diff --git a/modules/authority/services/local/code/plugins/installation/offers.go b/modules/authority/services/local/code/plugins/installation/offers.go index ccd94355..c682a843 100644 --- a/modules/authority/services/local/code/plugins/installation/offers.go +++ b/modules/authority/services/local/code/plugins/installation/offers.go @@ -105,7 +105,10 @@ func (p *Plugin) review(offer authority.ReviewedOffer, bindings map[string]*runn if err != nil { return nil, authorityplugin.Status(err) } - out := &installationv1.ReviewedOffer{Target: projected.Target, ToolsComposed: projected.ToolsComposed} + out := &installationv1.ReviewedOffer{Target: projected.Target, ToolsComposed: projected.ToolsComposed, InstallationRole: projected.InstallationRole} + for _, d := range projected.EdgeDependencies { + out.EdgeDependencies = append(out.EdgeDependencies, &installationv1.OfferDependency{Module: d.Module, Presence: d.Presence}) + } if a := projected.Approval; a != nil { out.Approval = &installationv1.ConsentApproval{TargetId: a.TargetID, Generation: a.Generation, ExpectedRevision: a.ExpectedRevision, Digest: a.Digest} } diff --git a/modules/authority/services/local/code/plugins/registry_test/solution_principal_target_test.go b/modules/authority/services/local/code/plugins/registry_test/solution_principal_target_test.go index 508d6e59..89cf74c4 100644 --- a/modules/authority/services/local/code/plugins/registry_test/solution_principal_target_test.go +++ b/modules/authority/services/local/code/plugins/registry_test/solution_principal_target_test.go @@ -28,8 +28,10 @@ func TestTheLocalAuthorityMeetsTheSolutionPrincipalTarget(t *testing.T) { k := testkit.New(t) // Two people whose own organisation grants reach one existing resource of // the store's kind (one administers it, one reads it), and a member of a - // team no installation is granted to. - if err := k.State.LoadFixture(context.Background(), []byte(`roles: + // team no installation is granted to, and a team nobody belongs to. + if err := k.State.LoadFixture(context.Background(), []byte(`teams: + - {organization: acme, id: principal-empty-team} +roles: - organization: acme name: principal-store-admin scopes: @@ -84,7 +86,7 @@ people: Store: "principal-store", StoreCredential: k.Credential(t, "principal-store"), Kind: "principal.collection", LegacyStore: "principal-legacy-store", LegacyStoreCredential: k.Credential(t, "principal-legacy-store"), LegacyKind: "principal.legacy", Admin: "fixture-alex", Viewer: "fixture-maya", Editor: "fixture-remi", Member: "fixture-dev", Installer: "fixture-principal-installer", - Team: "principal-team", TeamMember: "fixture-principal-team-member", TeamLeaver: "fixture-principal-team-leaver", OrgLeaver: "fixture-principal-org-leaver", + Team: "principal-team", EmptyTeam: "principal-empty-team", TeamMember: "fixture-principal-team-member", TeamLeaver: "fixture-principal-team-leaver", OrgLeaver: "fixture-principal-org-leaver", Administrator: "fixture-store-administrator", Reader: "fixture-store-reader", NoteReader: "fixture-principal-note-reader", OtherAdmin: "fixture-zoe", OtherMember: "fixture-omar", OtherTeam: "ops", Existing: "store-x", diff --git a/modules/authority/services/local/openapi/host/authority/v1/authority.swagger.json b/modules/authority/services/local/openapi/host/authority/v1/authority.swagger.json index aa06169b..4ff83cda 100644 --- a/modules/authority/services/local/openapi/host/authority/v1/authority.swagger.json +++ b/modules/authority/services/local/openapi/host/authority/v1/authority.swagger.json @@ -128,11 +128,11 @@ }, "method": { "type": "string", - "description": "The method the capability is presented to: \"/package.Service/Method\" for a\ngRPC call, \"\u003cVERB\u003e \u003cpath\u003e\" for an HTTP request (GET /v1/model/profiles),\nmatched against the routes the module declares. When set, the check also\napplies what the audience module's delivery declares for that method, and\nrefuses by name a call that does not hold it, a method that declares\nnothing, and a method the module does not serve." + "description": "The method the capability is presented to: \"/package.Service/Method\" for a\ngRPC call, \"\u003cVERB\u003e \u003cpath\u003e\" for an HTTP request (GET /v1/model/profiles),\nmatched against the routes the module declares. When set, the check also\napplies what the audience module's delivery declares for that method, and\nrefuses by name a call that does not hold it, a method that declares\nnothing, and a method the module does not serve.\nTwo exact methods are reserved for installation setup instead of delivered\nmethod policy: \"POST /_codefly/installations\" and\n\"/host.installation.v1.InstallableService/Install\". They accept only the\nsetup-only credential for the exact current consented pending installation;\nordinary capabilities are refused, even if delivered policy permits them.\nThe receiver supplies its actual method and its own delivered audience,\nnever values selected by the request body. The verified organization and\ninstallation seal must match the setup body and carriers before callback\nexecution or a cached acknowledgement. Setup admits no ordinary data work." }, "advisory": { "type": "boolean", - "description": "Advisory asks for the policy decision without its refusal: the capability\nis still verified and refused when it is not sound, but what the method\ndeclares is only reported, in CheckResponse.refusal, and the verified\ncapability is answered. A hop in a composition that runs permissive\n(hop/ENFORCEMENT=warn) asks this way and logs the refusal it did not make.\nHosts may refuse advisory outside local development runs." + "description": "Advisory asks for the policy decision without its refusal: the capability\nis still verified and refused when it is not sound, but what the method\ndeclares is only reported, in CheckResponse.refusal, and the verified\ncapability is answered. A hop in a composition that runs permissive\n(hop/ENFORCEMENT=warn) asks this way and logs the refusal it did not make.\nHosts may refuse advisory outside local development runs.\nAt either reserved setup method, advisory does not relax setup credential\nauthentication or its pending-attempt checks: a refusal is always an error." } }, "description": "CheckRequest presents a capability for verification." diff --git a/modules/authority/services/local/proto/host/authority/v1/authority.proto b/modules/authority/services/local/proto/host/authority/v1/authority.proto index c9c9a99d..4df9e58a 100644 --- a/modules/authority/services/local/proto/host/authority/v1/authority.proto +++ b/modules/authority/services/local/proto/host/authority/v1/authority.proto @@ -274,6 +274,15 @@ message CheckRequest { // applies what the audience module's delivery declares for that method, and // refuses by name a call that does not hold it, a method that declares // nothing, and a method the module does not serve. + // Two exact methods are reserved for installation setup instead of delivered + // method policy: "POST /_codefly/installations" and + // "/host.installation.v1.InstallableService/Install". They accept only the + // setup-only credential for the exact current consented pending installation; + // ordinary capabilities are refused, even if delivered policy permits them. + // The receiver supplies its actual method and its own delivered audience, + // never values selected by the request body. The verified organization and + // installation seal must match the setup body and carriers before callback + // execution or a cached acknowledgement. Setup admits no ordinary data work. string method = 3; // Advisory asks for the policy decision without its refusal: the capability // is still verified and refused when it is not sound, but what the method @@ -281,6 +290,8 @@ message CheckRequest { // capability is answered. A hop in a composition that runs permissive // (hop/ENFORCEMENT=warn) asks this way and logs the refusal it did not make. // Hosts may refuse advisory outside local development runs. + // At either reserved setup method, advisory does not relax setup credential + // authentication or its pending-attempt checks: a refusal is always an error. bool advisory = 4; } diff --git a/modules/authority/services/local/proto/host/composition/v1/composition.proto b/modules/authority/services/local/proto/host/composition/v1/composition.proto index a834fd4c..f0f10078 100644 --- a/modules/authority/services/local/proto/host/composition/v1/composition.proto +++ b/modules/authority/services/local/proto/host/composition/v1/composition.proto @@ -67,6 +67,19 @@ message AuthorityManifestV1 { // delivery withdrawing it refuses the owner-selected capabilities issued // before. Every solution receiving its roles' scopes declares it. bool owner_aware = 7; + // Core-validated selector, carried verbatim: empty is legacy caller selection, + // "none" declares no standing machine authority, otherwise a declared role. + string installation_role = 8; + // Viewer-edge presence only; grants no authority. Nil and empty are equivalent. + repeated EdgeDependencyV1 edge_dependencies = 9; +} + +// EdgeDependencyV1 names the exact signed module principal. The host resolves +// it to a live catalogue target; it is not a target UUID, route alias or binding id. +message EdgeDependencyV1 { + string module = 1; + // Core validates required or optional; delivery does not default this value. + string presence = 2; } // MethodPolicyV1 is what a call to one method must hold, as the module's diff --git a/modules/authority/services/local/proto/host/installation/v1/installation.proto b/modules/authority/services/local/proto/host/installation/v1/installation.proto index f3f3f139..a2edbc62 100644 --- a/modules/authority/services/local/proto/host/installation/v1/installation.proto +++ b/modules/authority/services/local/proto/host/installation/v1/installation.proto @@ -264,6 +264,19 @@ message ReviewedOffer { // False when this run delivered no prepared Runnable bindings: no tool is // shown, which says nothing about what a composed binding would expose. bool tools_composed = 6; + // Signed machine-role selector: empty is undeclared, "none" is no standing + // authority, otherwise a named role. This is not a person or team grant. + string installation_role = 7; + // Exact declared viewer-edge presence, covered by the reviewed digest. + // Required access and per-module approval are separate host review concerns. + repeated OfferDependency edge_dependencies = 8; +} + +// OfferDependency carries presence only, never scopes or inferred binding ids. +// Module is the exact signed principal, resolved by the host to its live target. +message OfferDependency { + string module = 1; + string presence = 2; } // OfferBinding is one declared call from the solution to a module. diff --git a/tools/noproxygate/generated.json b/tools/noproxygate/generated.json index 0558031c..f99805da 100644 --- a/tools/noproxygate/generated.json +++ b/tools/noproxygate/generated.json @@ -3,12 +3,12 @@ "libraries/authority-client/go/gen/api_grpc.pb.go": "71617cc6107e9332fa88dc1889892378cf36e0887a5d05b87e7af8089a158f5a", "libraries/authority-client/go/gen/host/artifact/v1/artifact.pb.go": "8f032d82c2a6921e47ec08cde4c797fa75ec05e6c7cccb5b5a1be744805e8f4e", "libraries/authority-client/go/gen/host/artifact/v1/artifact_grpc.pb.go": "d30b417db566534c3cb3e8a0ce4410ba0660698a8dcbeb83b36ac2fb726810e0", - "libraries/authority-client/go/gen/host/authority/v1/authority.pb.go": "492cf6c05052878ae2fc37b5f9ee1c647d796911808ad29cdb984789e337f915", + "libraries/authority-client/go/gen/host/authority/v1/authority.pb.go": "74250cd059da04bdbbc5762a34221d98773c10c31189c9db591c08b588681e07", "libraries/authority-client/go/gen/host/authority/v1/authority_grpc.pb.go": "8423702eea66abf48b9feb82778bd80f0669102156588104f16bad8734107cfc", "libraries/authority-client/go/gen/host/authority/v1/work_context.pb.go": "c53b6b47355ee9a502441173931b3b809e510054168aa0cc2984b5152e771bf1", - "libraries/authority-client/go/gen/host/composition/v1/composition.pb.go": "8fc4657b7b207b2fd80db74b238b10e5b0afd65b25851d56817cb0b960b1c750", + "libraries/authority-client/go/gen/host/composition/v1/composition.pb.go": "cddeb5d3b921b475e8aadaafbe343ef4d3091f745eb13ccd534811e5f4c802a3", "libraries/authority-client/go/gen/host/composition/v1/composition_grpc.pb.go": "acd95033d684741c23c74eb9f05021ebe487b9ab9e9cd81d549d44ab6c6d6162", - "libraries/authority-client/go/gen/host/installation/v1/installation.pb.go": "194a3aedd722c07b5b569c374331a60b073ae7e97d8a6181758518c03a315474", + "libraries/authority-client/go/gen/host/installation/v1/installation.pb.go": "ee0998bbcc54b9443d88565dbb55a0cc7b40be0429298fad99bad35479e2ad62", "libraries/authority-client/go/gen/host/installation/v1/installation_grpc.pb.go": "b281d56d7e134fe79fd623595e6f63908bb91694fbac9127994dcfc05a801a42", "libraries/authority-client/go/gen/host/session/v1/session.pb.go": "ef682bffabfc1011bac310513c408562e2b4027e5293c43dde69efd124fc4759", "libraries/authority-client/go/gen/host/session/v1/session_grpc.pb.go": "c4177166bf12cacb6e171beb4f739e730b7f005d559669cbab8be51598796468", @@ -39,15 +39,15 @@ "modules/authority/services/local/code/pkg/gen/host/artifact/v1/artifact.pb.go": "13863e4c49e9b038ea946ac6c2a373dc222f5b1fb8ddc437109c9bd2afa7bb3f", "modules/authority/services/local/code/pkg/gen/host/artifact/v1/artifact_grpc.pb.go": "d30b417db566534c3cb3e8a0ce4410ba0660698a8dcbeb83b36ac2fb726810e0", "modules/authority/services/local/code/pkg/gen/host/artifact/v1/artifactv1connect/artifact.connect.go": "8d5ab439f1293c76d3b5d2fe0e2a4845694a08b007f13c62f19c2de6c6e2b20f", - "modules/authority/services/local/code/pkg/gen/host/authority/v1/authority.pb.go": "cec19ac17902c944dad058a010b9681d3b3f3458221027d279ac40c7eeb682e0", + "modules/authority/services/local/code/pkg/gen/host/authority/v1/authority.pb.go": "ceed1aced58f9b9795bfab5c19657c74db8854c2663e4b3e59cb544c05fa57f4", "modules/authority/services/local/code/pkg/gen/host/authority/v1/authority.pb.gw.go": "37a1508e28e8bdb5310471401c9f78a5b14faa646e826bc5592ef64684fb7ea2", "modules/authority/services/local/code/pkg/gen/host/authority/v1/authority_grpc.pb.go": "8423702eea66abf48b9feb82778bd80f0669102156588104f16bad8734107cfc", "modules/authority/services/local/code/pkg/gen/host/authority/v1/authorityv1connect/authority.connect.go": "5d0260cc1c302f6a42117706c3354efa8f4299846f78550d17685756da580a5e", "modules/authority/services/local/code/pkg/gen/host/authority/v1/work_context.pb.go": "30da6150c9d3567ba0552590dd4ae3736f313097d4f4df8fcbaf242fca8af029", - "modules/authority/services/local/code/pkg/gen/host/composition/v1/composition.pb.go": "c7a1220dc4bfeb3419485a4b41b5a8a5f8a9a35654180402b46010345f5094e4", + "modules/authority/services/local/code/pkg/gen/host/composition/v1/composition.pb.go": "d8829594e15e28e28d3ae5acd79fb06dc8b88468fffbb9c0aec9cefd23283a05", "modules/authority/services/local/code/pkg/gen/host/composition/v1/composition_grpc.pb.go": "acd95033d684741c23c74eb9f05021ebe487b9ab9e9cd81d549d44ab6c6d6162", "modules/authority/services/local/code/pkg/gen/host/composition/v1/compositionv1connect/composition.connect.go": "d1c06a0ee8ffad586e36271b773e52a43737e49c4473207e1fa03b922f3fe88f", - "modules/authority/services/local/code/pkg/gen/host/installation/v1/installation.pb.go": "d78330fd7ab955915decc72e93d56b2e8bda7e2bd078f46a37b15723f7bd1117", + "modules/authority/services/local/code/pkg/gen/host/installation/v1/installation.pb.go": "6f0d2746776f3b815e7c648648c35a8400f24e9b175c9ee60eb161e6f09e8e2d", "modules/authority/services/local/code/pkg/gen/host/installation/v1/installation_grpc.pb.go": "b281d56d7e134fe79fd623595e6f63908bb91694fbac9127994dcfc05a801a42", "modules/authority/services/local/code/pkg/gen/host/installation/v1/installationv1connect/installation.connect.go": "e97cdef0c8c33949f92b886289ecbfa708ea23199e6ff637f9230b46fce4d40d", "modules/authority/services/local/code/pkg/gen/host/session/v1/session.pb.go": "142a830fe5d9b3418c5124cb520486545cdbf6003382994055b587e7c46de1c1",