Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions connection-coordinator/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ paths:
$ref: paths/environments.yaml#/OneEnvironment
/providers/{provider}/environments/{environment}/ConfirmActivationKey:
$ref: paths/environments.yaml#/ConfirmActivationKey
/providers/{provider}/environments/{environment}/activationKeyParameters:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason to prefer a custom method instead of adding the parameters as attributes on the environment itself?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kept it as a separate resource rather than folding onto Environment, for two reasons: (1) Environment is also returned by ListEnvironments, so putting rotating key material on that schema means it either rides along on every list page or needs omit-on-list handling; and (2) the public key + nextRotationAt rotate ~weekly while the rest of Environment is near-static — a dedicated endpoint lets the key material carry its own cache/authz semantics instead of forcing short TTLs on the whole environment read. Both shapes are additive and backward-compatible since this is net-new, so it came down to keeping the rotating bits isolated. Happy to fold it in if you feel the single-call ergonomics outweigh the list/caching coupling.

$ref: paths/environments.yaml#/ActivationKeyParameters
/providers/{provider}/environments/{environment}/interconnects:
$ref: paths/interconnects.yaml#/AllInterconnects
/providers/{provider}/environments/{environment}/interconnects/{interconnect}:
Expand Down
25 changes: 25 additions & 0 deletions connection-coordinator/paths/environments.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,28 @@ ConfirmActivationKey:
security: []
tags:
- Environments

ActivationKeyParameters:
get:
description: |-
Returns the activation key parameters for an environment. This includes
the supported activation key versions and encryption details for
constructing version 2 (encrypted) keys.

Key creators should call this endpoint (or use a recently cached response)
before constructing an activation key destined for this environment.
operationId: GetActivationKeyParameters
parameters:
- $ref: "../parameters/_index.yaml#/parameters/x-request-id"
- $ref: "../parameters/_index.yaml#/parameters/provider"
- $ref: "../parameters/_index.yaml#/parameters/environment"
responses:
default:
content:
application/json:
schema:
$ref: "../schemas/environment.yaml#/GetActivationKeyParametersResponse"
description: Successful operation
security: []
tags:
- Environments
196 changes: 176 additions & 20 deletions connection-coordinator/schemas/environment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -205,44 +205,133 @@ ConfirmActivationKeyRequest:

ActivationKey:
description: |-
Activation Keys contain all information needed by a provider to validate a
activation proposal between two provider.
The decoded activation key structure. The key is always transported as a
base64-encoded JSON string between providers. After decoding, the `version`
field determines which variant applies.

The activation key should be base64 encoded whenever it is exchanged between
providers. This helps prevent accidental corruption of the key when
transferring it between clouds.
All versions share a common envelope containing `version` and
`destinationEnvironmentUri`, declared here. The remaining fields differ by
version and are contributed by the matching `oneOf` variant.
type: object
required:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For cleanliness, I'd suggest you just put the properties for version / destinationEnvironmentUri here instead of each oneof reference

  properties:
    version: ...
    destinationEnvironmentUri: ...
  oneOf:
    - $ref: "$/V1"
    - $ref: "$/V2"

should work correctly because oneOf refs get merged in at the same depth as where the 'oneOf' is.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call — done in 256c6b4. Hoisted version and destinationEnvironmentUri onto the ActivationKey base as properties alongside the oneOf; the variants now only contribute their variant-specific fields. V1/V2 keep the version enum constraint so the oneOf stays unambiguous.

Confirmed it's valid OpenAPI 3.0 and backward compatible — I validated the base+oneOf structure against a pre-PR v1 key (all fields flat, version: 1), a v2 key, and a negative control (v1 body with version: 2): v1 and v2 both validate, the mismatch is correctly rejected. One note for the future: this relies on NOT setting additionalProperties: false on the base, since the base and the branches are evaluated independently — a base that forbade extras would reject the branch-only fields (encryptedContents, etc.).

- version
- destinationEnvironmentUri
properties:
version:
description: |-
Required. Version of the ActivationKey itself. The version should
match the API version on the creating service.
format: int32
Required. The activation key version. Determines which variant
(ActivationKeyV1 or ActivationKeyV2) applies.
type: integer
format: int32
destinationEnvironmentUri:
description: |-
Required. The destination environment URI this activation key is
intended for use at.
intended for use at. Always in cleartext so the receiver can identify
which environment (and, for version 2, which private key) to use.
type: string
discriminator:
propertyName: version
mapping:
"1": "#/ActivationKeyV1"
"2": "#/ActivationKeyV2"
oneOf:
- $ref: "#/ActivationKeyV1"
- $ref: "#/ActivationKeyV2"

ActivationKeyV1:
description: |-
Version 1 activation key. All fields are present in cleartext within the
base64-decoded JSON structure. In addition to the shared envelope fields
(`version`, `destinationEnvironmentUri`), this variant carries the
connection parameters directly.
type: object
required:
- version
- destinationEnvironmentUri
- sharedConnectionUuid
- connectionSizeMbps
- destinationAccountId
properties:
version:
description: Activation key version. Must be `1` for this variant.
type: integer
format: int32
enum: [1]
sharedConnectionUuid:
description: Required. The UUID assigned to this connection to be used by
both services.
description: |-
Required. The UUID assigned to this connection to be used by both
services.
type: string
connectionSizeMbps:
description: Required. Size of the connection to be provisioned in mbps.
format: int32
description: Required. Size of the connection to be provisioned in Mbps.
type: integer
format: int32
destinationAccountId:
description: |-
Required. User supplied account id/number on the destination CSP.
Receiving service MUST verify that the activation key was provided from an authorized user of this account.
Receiving service MUST verify that the activation key was provided
from an authorized user of this account.
type: string

ActivationKeyV2:
description: |-
Version 2 activation key. Sensitive fields are encrypted using the
destination environment's public key. Only the shared envelope fields
(`version`, `destinationEnvironmentUri`) remain in cleartext to enable
routing and key selection for decryption.
type: object
required:
- connectionSizeMbps
- destinationAccountId
- destinationEnvironmentUri
- sharedConnectionUuid
- version
- version
- destinationEnvironmentUri
- encryptedContents
properties:
version:
description: Activation key version. Must be `2` for this variant.
type: integer
format: int32
enum: [2]
encryptedContents:
description: |-
Required. Base64-encoded ciphertext containing the encrypted inner
payload. The payload is encrypted using the destination environment's
public key (obtained via GetActivationKeyParameters). After
decryption, the plaintext is a JSON object conforming to
ActivationKeyEncryptedPayload.
type: string
format: byte

ActivationKeyEncryptedPayload:
description: |-
The plaintext JSON structure contained within the `encryptedContents` field
of a version 2 ActivationKey, after decryption. Contains all sensitive
connection parameters that are not visible in the outer envelope.
type: object
required:
- sharedConnectionUuid
- connectionSizeMbps
- destinationAccountId
- sourceAccountId
properties:
sharedConnectionUuid:
description: |-
Required. The UUID assigned to this connection to be used by both
services.
type: string
connectionSizeMbps:
description: Required. Size of the connection to be provisioned in Mbps.
type: integer
format: int32
destinationAccountId:
description: |-
Required. User supplied account id/number on the destination CSP.
Receiving service MUST verify that the activation key was provided
from an authorized user of this account.
type: string
sourceAccountId:
description: |-
Required. Account id/number of the originating (creating) provider's
user on the source CSP. Introduced in version 2 activation keys.
type: string

ConfirmActivationKeyResponse:
description: |-
Expand All @@ -254,4 +343,71 @@ ConfirmActivationKeyResponse:
keyValid:
description: Indicates if the remote CSP believes this key is valid.
type: boolean
type: object
type: object

GetActivationKeyParametersResponse:
description: |-
Response describing the activation key parameters for an environment.
Contains the supported activation key versions and encryption details
(if the environment supports encrypted keys).
type: object
required:
- supportedVersions
properties:
supportedVersions:
description: |-
The activation key versions this environment supports. The key creator
should prefer the highest version number in this list. For example,
`[1, 2]` indicates both plaintext and encrypted keys are accepted,
with version 2 (encrypted) preferred.
type: array
items:
type: integer
format: int32
encryption:
allOf:
- $ref: "#/EncryptionParameters"
description: |-
Encryption parameters for constructing a version 2 activation key.
Present only when the environment supports version 2 keys. Absence of
this field indicates the environment only supports version 1 (plaintext)
keys.

EncryptionParameters:
description: |-
Encryption details required to construct a version 2 (encrypted) activation
key. The key creator uses these parameters to encrypt the inner payload
before constructing the activation key.
type: object
required:
- publicKey
- algorithm
- keyFormat
properties:
publicKey:
description: |-
The PEM-encoded public key for encrypting activation key contents
destined for this environment. Providers are recommended to rotate at
least weekly, though the rotation cadence is ultimately agreed between
the two partners based on their security needs. The environment accepts
keys encrypted with any of the 4 most recent public keys.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe rotation frequency should actually be included as a parameter? Helps inform the caller how often they should be pulling a new key, assuming some providers want more frequent rotations.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than a frequency parameter, I've added nextRotationAt — a UTC date-time telling the caller approximately when this environment next rotates, so they know precisely how long a fetched key stays usable (better than inferring from a cadence). Made it optional since the receiver accepts any of the 4 most recent keys regardless, so a provider that doesn't publish a schedule isn't forced to. Pushed in 31e0a59.

type: string
nextRotationAt:
description: |-
The UTC timestamp at which this environment will next rotate its
public/private key pair. Informs the caller when to fetch a fresh
public key; a cached key remains usable until then. Optional -- absent
if the environment does not expose a scheduled rotation time.
type: string
format: date-time
example: "2026-09-08T00:00:00Z"
algorithm:
description: |-
The asymmetric encryption algorithm to use when encrypting the inner
payload with the provided public key.
type: string
example: "RSA-OAEP-256"
keyFormat:
description: The encoding format of the public key.
type: string
example: "PKCS8"