# @serve.zone/interfaces

`@serve.zone/interfaces` is the shared TypeScript contract package for the serve.zone ecosystem. It contains the public data shapes and TypedRequest interfaces used by Cloudly, Coreflow, Spark, Coretraffic, platform clients, SDKs, and external integrations to exchange infrastructure state without duplicating DTOs.

## Issue Reporting and Security

For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.

## Install

```bash
pnpm add @serve.zone/interfaces
```

## Public API

The root export exposes five namespaces:

```typescript
import { appstore, data, platform, platformservice, requests } from '@serve.zone/interfaces';
```

| Namespace | Purpose |
| --- | --- |
| `appstore` | App Store catalog, manifest, service requirement, and upgrade contracts. |
| `data` | Durable platform object shapes such as clusters, services, deployments, images, domains, DNS entries, secrets, users, status, settings, backups, registries, BaseOS metadata, and task executions. |
| `requests` | TypedRequest contracts for Cloudly and serve.zone control-plane RPC methods. |
| `platform` | Current platform-service contracts for email, SMS, push notifications, letters, AI, databases, object storage, logging, backups, and SIP. |
| `platformservice` | Legacy platform-service namespace kept for older consumers that still depend on the previous layout. |

This package intentionally has no service implementation logic. It is a stable vocabulary for services that need to agree on payload shape, method names, and response types.

WorkloadInit release verifiers should use the dedicated Node-compatible subpath:

```typescript
import {
  validateWorkloadInitReleaseAttestationStatement,
  validateWorkloadInitReleaseIdentity,
  workloadInitReleaseContract,
} from '@serve.zone/interfaces/runtime/workloadinit';
```

`@serve.zone/interfaces/runtime/workloadinit` exposes only the WorkloadInit
release identity, attestation, approval, authority, digest, and validator
contracts. Its runtime dependency graph is limited to the immutable-image digest
validator; unlike `@serve.zone/interfaces/runtime`, it does not load the general
runtime graph, `plugins.js`, SmartCrypto, TypedRequest, Corestore, or settlement
runtime modules.

Service creation carries canonical ownership separately from caller-writable
service data. `requests.service.IRequest_Any_Cloudly_CreateService` requires a
top-level `organizationId`, while `data.TServiceWritableData` excludes the
server-managed ownership field. Consumers should use
`data.validateOrganizationId()` to validate the identifier shape and verify
the request's ownership authorization separately before persistence.

## Secrets v24 Pre-Cutover Contract

Version 24 retains the value-free v23 architecture while replacing the remaining
pre-cutover lifecycle and runtime authority gaps. Secret values enter Cloudly
only as strict SmartCrypto X25519 envelopes or an explicit bounded
server-generation request. Coreflow receives a full digest-verified schema-v2
manifest plus one sealed envelope per pinned SecretVersion through the Node-only
runtime export.

This is a contract release before consumer cutover. It does **not** claim that
clean-v2 migration, backup verification, historical secret erasure, or storage
cleanup has completed. Those operations remain separately gated and must be
proven by their owning services before destructive cleanup.

Breaking removals include:

- The SecretGroup and SecretBundle data/request modules and request namespaces.
- `getServiceSecretBundlesAsFlatObject` and every service/preflight field tied
  to bundled flattening or aggregate runtime files.
- Plaintext resolved runtime values, generic platform config/credential maps,
  credential-bearing Cloudly settings, and serialized object-storage
  credential references.
- Hosted-app control-token identities and credential-bearing bootstrap actions.

The current secret contract includes:

- `TSecretValueInput`, active-only `IActiveSecretRecipientMetadata`, the
  `requests.secret.IReq_GetSecretIngressRecipient` contract with method
  `getSecretIngressRecipient`, and fixed-order create/rotate/App Store context
  builders.
- `ISecretEnvelopeAdmissionBindingV1` under the Node-only runtime export binds
  an exact envelope and request context to one recipient generation. The
  binding is reproducible and does not itself prove admission. Retiring-key
  retries require a trusted Cloudly mutation receipt created atomically while
  that recipient was active; caller-supplied admission or issuance timestamps
  are not part of the contract.
- `getSecretVersionPurgePreflight` returns bounded advisory reference pages and
  complete blocker counts. `purgeSecretVersion` identifies one exact version
  and fences the mutation by secret, version, and target revisions. The
  non-issuable `purge-pending` lifecycle and revisioned
  `pending`/`erasing`/`failed`/`succeeded` operation keep external erasure
  durable across retries without treating preflight as authorization.
- Schema-v2 `IResolvedSecretManifest` contracts, stable Docker resource naming,
  WorkloadInit map/wrapper helpers, and `ISealedResolvedSecretMaterial` under
  `@serve.zone/interfaces/runtime`.
- Two-step Coreflow X25519 recipient enrollment and exact recipient lifecycle
  validators.
- Mandatory `IImmutableContainerInvocationV1` evidence on immutable image
  deployment plans.

### Secrets v24 Runtime Registration And Reporting

The Node-only `@serve.zone/interfaces/runtime` export adds the live contract
that Cloudly must validate before publishing secret-bearing desired state to a
Coreflow connection:

- `getSecretRecipientEnrollmentState` returns either an exact generation-zero
  empty state or the complete valid recipient set for the cluster derived from
  the verified JWT.
- `getCoreflowSecretRuntimeRegistrationExpectation` returns either an
  `available` v2 expectation or an explicit unavailable reason. The available
  expectation binds the live reporter session, active recipient, fresh
  generation-fenced target authority, and active WorkloadInit approval through
  `expectationDigest`.
- Spark sends authenticated, sequenced local Swarm membership observations and,
  on managers, complete manager snapshots. Cloudly derives scope from the Spark
  credential, reconciles manager consensus privately, and publishes one fresh
  single-Swarm target authority or a targetless unavailable state. Structural
  contract validation does not itself establish manager consensus.
- `TSparkSwarmObservationV2` lets workers report only their local Swarm node ID,
  because Docker does not expose the Swarm cluster ID to workers. Cloudly may
  associate that node with a cluster only through authenticated node scope and
  accepted manager consensus. The v2 transport contract designates
  `/spark/v2/nodes/swarm-observation`. A conforming Spark sender must retain one
  exact request until it receives a request-bound acceptance receipt. A
  conforming Cloudly consumer must authenticate the node before inspecting
  replay state, derive cluster scope from that persisted identity, and never
  register, translate, or accept v1 observations as an authority fallback.
- `computeSparkSwarmObservationV2Digest` uses strict canonical JSON and the
  `serve.zone/spark-swarm-observation-v2` domain. A reporter session starts at
  sequence one. The contract requires a Cloudly consumer to accept only the
  exact next sequence with a strictly advancing `observedAt`, while an exact
  same-sequence/same-digest retry returns the byte-equivalent persisted receipt
  before age checks. It must reject conflicting, stale, skipped,
  retired-session, old, future, or non-advancing reports. A reused session ID
  may be treated as new only after it leaves the bounded retired window, and its
  sequence-one observation must still advance the permanent timestamp
  high-water mark. The consumer must persist acceptance state and its receipt
  in one atomic boundary.
- `validateSparkSwarmObservationV2` validates exact structure and attached
  snapshots, while `validateSparkSwarmObservationV2Request` and
  `validateSparkSwarmObservationV2Response` validate transport and request
  binding. None of these functions authenticates a node, persists replay state,
  establishes manager consensus, or creates runtime authority.
- WorkloadInit approval binds a clean stable release identity, version-tagged
  OCI index, exact amd64 and arm64 platform/executable digests, policy
  generation, and a Cloudly summary of detached Cosign DSSE/SLSA verification.
  Public shape and digest validators do not verify the signature, public-key
  trust root, or private Cloudly policy.
- `coreflowSecretRuntimeRegistrationTagId` is the sole dedicated TypedSocket
  tag identifier. Its payload is exactly
  `ICoreflowSecretRuntimeRegistrationV2`; cluster scope comes from the verified
  connection identity rather than the tag.
- `ICoreflowSecretRuntimeRegistrationV2` references the exact expectation,
  target generation/digest, and WorkloadInit authority. It must cover every
  Cloudly-node/Swarm-cluster/Swarm-node identity and approved per-platform
  manifest and installed-executable digest without self-asserting placement or
  approval.
- `validateCoreflowSecretRuntimeRegistration` compares the registration with a
  trusted expectation built by Cloudly, including the live reporter session.
  Missing, extra, duplicate, reordered, or mismatched node evidence fails
  closed. Consumers must discard the registration on transport disconnect, tag
  removal or replacement, or any live session, placement, artifact, or
  recipient expectation change before publishing more secret-bearing state.
- `reportSecretDeploymentState` reports only `applying`, `applied`, `drifted`,
  or `failed` for the manifest and plan revision selected by Cloudly. The
  validator receives the trusted cluster ID after JWT verification and the
  trusted live reporter session. Wire-provided manifest scope is checked
  against, and never replaces, that trusted authority.

The package validates report shape, digest, trusted cluster, and trusted live
session; it does not persist replay state or mutate deployment plans. Deployment
report consumers must persist an atomic receipt keyed by the verified cluster,
reporter session, service, and positive sequence. The report digest uses
fixed-order JSON and excludes only the JWT identity and `reportDigest`. The
consumer accepts the exact next sequence once, returns the prior response for a
same-digest replay, rejects a different-digest replay, and ensures a plan
revision CAS failure consumes neither the sequence nor a receipt. Timestamps
are informational and never replace live session, placement, artifact,
recipient, sequence, or plan-revision fences.

`createWorkloadInitEnvironmentMap` now rejects manifests without any
`launcher-environment` delivery. Consumers must bypass WorkloadInit for
file-only manifests.

## Portable Storage Contracts

App Store templates can declare logical, template-local `storageClasses` and
stable named `storageRequests`. The same manifest is fulfilled by Onebox or
Cloudly without exposing a physical provider:

```typescript
const storageConfig: appstore.IAppStoreVersionConfig = {
  image: 'example/database:1.0.0',
  port: 5432,
  storageClasses: {
    databaseFast: {
      kind: 'filesystem',
      purpose: 'database',
      required: {
        performanceTier: 'highIops',
        durability: 'persistent',
        hardQuota: true,
        snapshots: 'native',
        encryptedInTransit: true,
      },
    },
    backupCapacity: {
      kind: 'objectStorage',
      purpose: 'backup',
      required: {
        performanceTier: 'capacity',
        durability: 'persistent',
        hardQuota: true,
        encryptedInTransit: true,
      },
    },
  },
  storageRequests: [
    {
      id: 'database-data',
      kind: 'filesystem',
      storageClass: 'databaseFast',
      mountPath: '/var/lib/example',
      accessMode: 'ReadWriteOnce',
      capacity: { request: '20GiB', limit: '40GiB' },
      reclaimPolicy: 'retain',
      protection: { backup: 'required', snapshots: 'native' },
    },
    {
      id: 'backup-archive',
      kind: 'objectStorage',
      storageClass: 'backupCapacity',
      accessMode: 'readWrite',
      capacity: { request: '100GiB', limit: '1TiB' },
      reclaimPolicy: 'retain',
      delivery: {
        type: 'file',
        targetPath: '/run/secrets/backup-archive.json',
        format: 'servezone-object-storage-v1',
        uid: 1000,
        gid: 1000,
        mode: 0o400,
      },
      protection: { versioning: 'required', retentionDays: 30 },
    },
  ],
  requiresFeatures: [
    appstore.appStoreStorageFeatureIds.bindingsV2,
    appstore.appStoreStorageFeatureIds.filesystemV1,
    appstore.appStoreStorageFeatureIds.objectStorageV2,
    appstore.appStoreStorageFeatureIds.objectStorageFileV2,
  ],
};
```

Capacity quantities are positive integers followed by `KiB`, `MiB`, `GiB`, or
`TiB`. Storage request IDs survive upgrades and restores. Logical class keys
express requirements and preferences only; Onebox and Cloudly map them to
operator policy independently.

An app may declare multiple `objectStorage` requests. Each request resolves to
its own endpoint, bucket, and value-free credential management scope. Launcher
environment delivery uses an explicit key map and file delivery uses one
managed JSON Secret at a unique target path, so two
bindings cannot share credential destinations accidentally.

`platform.storage` contains separate capability advertisements and resolved
binding/status contracts. Resolved object-storage bindings expose connection
metadata plus a service-owned credential management scope and delivery policy,
never credential references or values. Filesystem
bindings expose the container mount and access mode, never a host path.

Portable manifests and resolved bindings intentionally have no fields for
Synology, NFS, Kerberos, Corestore, Kubernetes, Docker drivers, servers,
exports, mount options, provider credential values, or local fallback paths. Runtimes
must reject unknown manifest fields and unsupported required feature IDs before
provisioning. Legacy `volumes` and `platformRequirements.s3` remain deprecated
inputs for strict resolver normalization only.

`platform.storagemigration` defines the provider-neutral cutover contract for a
named object-storage binding. Corestore atomically owns and fences the source
binding after validating a distinct, unfenced active-object snapshot. Onebox
only receives a held candidate, stages that exact candidate, stops the matching
workload generation, and attests the quiesced state. The candidate cannot start
until `destinationBindingStartAuthorized` is true. Corestore continues returning
`consumerAction: 'startDestination'` until Onebox submits the mutation-fenced
consumer activation request and the acknowledgement becomes durable evidence.
Candidate-issued abort tombstones authorize only `startSource` and never retain
the staged candidate binding.

Every migration DTO and status has an exact, versioned runtime normalizer.
Unknown fields, provider or pool identifiers, unbounded strings, unsafe
integers, stale mutation revisions, identity drift, and lifecycle-inconsistent
fields are rejected. Migration-created digests use strict canonical JSON and a
bare lowercase 64-hex SHA-256 value; portable golden vectors cover the source
snapshot, target request, prepare intent, and candidate binding. Pre-cutover
failures may retry or abort; after the durable commit point, recovery can only
retry or roll forward. Physical pool IDs, mount details, provider receipts, and
publication capabilities remain private.

Primary exports include:

- `IObjectStorageMigrationPrepareRequest` and
  `TObjectStorageMigrationStatus` for the immutable intent and status journal.
- `IObjectStorageMigrationConsumerQuiesceRequest` with
  `IStorageMigrationConsumerQuiesceEvidence` for exact candidate staging and
  source-workload shutdown.
- `IObjectStorageMigrationConsumerActivationRequest` with
  `IStorageMigrationConsumerActivationEvidence` for durable destination-start
  acknowledgement.
- `normalizeObjectStorageMigrationStatus`,
  `bindObjectStorageMigrationConsumerQuiesceRequest`, and
  `bindObjectStorageMigrationConsumerActivationRequest` for strict ingress and
  current-revision mutation fencing.
- The `create*Sha256` helpers for source snapshots, prepare intent, target
  requests, candidate/active bindings, and persisted staging or activation
  evidence.

The phase and consumer-action progression is exact:

| Phase | `consumerAction` | Destination start authorized |
| --- | --- | --- |
| `preparing`, `transferring` | `wait` | No |
| `awaitingConsumerQuiesce` | `stageCandidateAndStop` | No |
| `finalizing`, `committing` | `wait` | No |
| `readyToStart` | `startDestination` | Yes |
| `cleanupPending`, `complete` | `none` | Yes; durable activation evidence is required |
| `aborting` | `wait` | No |
| `aborted` | `startSource` | No; only the active source binding may restart |

Normalize every status before acting, and bind consumer mutations to that exact
status revision:

```typescript
const status =
  await platform.storagemigration.normalizeObjectStorageMigrationStatus(
    untrustedStatusPayload,
  );

if (status.phase === 'awaitingConsumerQuiesce') {
  const request =
    await platform.storagemigration.bindObjectStorageMigrationConsumerQuiesceRequest(
      untrustedQuiescePayload,
      status,
    );
  await submitQuiesceAcknowledgement(request);
}

if (status.phase === 'readyToStart') {
  if (
    !status.destinationBindingStartAuthorized ||
    status.consumerAction !== 'startDestination'
  ) {
    throw new Error('destination binding is not authorized to start');
  }
  await startWorkload(status.activeBinding);
  const request =
    await platform.storagemigration.bindObjectStorageMigrationConsumerActivationRequest(
      untrustedActivationPayload,
      status,
    );
  await submitActivationAcknowledgement(request);
}

if (status.phase === 'cleanupPending') {
  // Cleanup is reachable only after this durable acknowledgement was accepted.
  const durableActivation = status.consumerActivationEvidence;
}

if (status.phase === 'aborted') {
  if (
    status.destinationBindingStartAuthorized ||
    status.consumerAction !== 'startSource'
  ) {
    throw new Error('invalid aborted migration status');
  }
  await startWorkload(status.activeBinding);
}
```

Here `startWorkload`, `submitQuiesceAcknowledgement`, and
`submitActivationAcknowledgement` are consumer-owned operations, not package
exports. Canonical digests are produced from normalized payloads:

```typescript
const snapshotSha256 =
  await platform.storagemigration.createUnfencedObjectStorageBindingControlSnapshotSha256(
    snapshotDigestPayload,
  );
const migrationSha256 =
  await platform.storagemigration.createObjectStorageMigrationSha256(
    prepareRequest,
  );
const candidateSha256 =
  await platform.storagemigration.createObjectStorageMigrationBindingSha256(
    candidateBinding,
  );
const activationRecordSha256 =
  await platform.storagemigration.createObjectStorageMigrationPersistedActivationSha256(
    activationDigestPayload,
  );
```

## Data Contracts

Use `data` when you need object shapes that are persisted, exchanged between services, or exposed through the Cloudly API.

```typescript
import { data } from '@serve.zone/interfaces';

const service: data.IService = {
  id: 'service-api',
  data: {
    name: 'api',
    description: 'Public API service',
    imageId: 'image-api',
    imageVersion: '1.0.0',
    environment: {
      NODE_ENV: 'production',
    },
    serviceCategory: 'workload',
    deploymentStrategy: 'limited-replicas',
    scaleFactor: 2,
    balancingStrategy: 'round-robin',
    targetPorts: [
      {
        name: 'web',
        port: 3000,
        protocol: 'http',
        default: true,
      },
      {
        name: 'ssh',
        port: 2222,
        protocol: 'ssh',
      },
    ],
    ports: {
      web: 3000, // legacy compatibility shorthand during migration
    },
    domains: [
      {
        name: 'api',
        protocol: 'https',
        targetPort: 'web',
      },
    ],
    publicPortMappings: [
      {
        name: 'ssh-public',
        publicPort: 2222,
        targetPort: 'ssh',
        protocol: 'tcp',
        exclusive: true,
      },
    ],
    deploymentIds: [],
  },
};
```

Common data contracts include:

- `ICluster` and `IClusterNode` for cluster membership and provisioning state.
- `IService`, `IDeployment`, `IImage`, `IRegistryTarget`, and `IExternalRegistry` for workload delivery.
- Service port contracts including `IServiceTargetPort`, `IServiceDomainRoute`, and `IServicePublicPortMapping` for canonical backend targets, domain target references, and edge/Coretraffic TCP/UDP public exposure.
- `IDomain`, `IDnsEntry`, and traffic contracts for routing and DNS management.
- Traffic and gateway route contracts including `ICoretrafficPortRouteConfig`, routing `portRoutes`, and `IGatewayClientRoute` client-owned route views. Gateway route intent supports optional match `domains`, `transport`, and `remoteIngress`, plus explicit route `priority` and `managedRouteKind`. Ownership can combine `hostname` with `routeRef` so a normal route and a path-specific managed route for the same hostname reconcile independently.
- Value-free `ISecretMetadata`, `ISecretVersionMetadata`, and `ISecretSetMetadata`
  contracts for operator views, plus schema-v2 exact-version
  `IResolvedSecretManifest` contracts for cluster delivery. Manifest helpers
  bind immutable image rollout and invocation evidence, enforce canonical
  digests and globally unique launcher/file targets, and track per-cluster
  desired/applied/previous-accepted rollout state. Platform-provider and system
  owners are valid metadata owners but are rejected from workload manifests.
  `listSecrets` exposes the dedicated `targetSecretsRevision` CAS fence;
  every create, rotate, and lifecycle mutation consumes and returns that
  aggregate owner fence, while
  `setServiceSecretSetAttachments` returns the independent
  `secretConfigurationRevision`. Generic service writes own neither revision.
  Purge is a separate exact-version mutation with its own version revision and
  durable operation; it is not a logical-secret lifecycle action.
- Mail gateway contracts for domain authorities, address bindings, WorkApp bindings, managed SMTP/API credentials, spool items, delivery journals, and inbound/outbound message payloads.
- Service-level mail configuration through `IService.data.mail`, including per-address inbound `smtpForward` settings and outbound credential metadata. Cloudly settings include dcrouter gateway, SMTP submission, and inbound forward-target keys for reconciling those bindings.
- Web Push contracts for environment-specific service bindings, public credential state, public VAPID key rotation metadata, privacy-minimal notification signals, and redacted delivery state. Subscription endpoints, browser key material, provider ciphertext, VAPID private keys, and credential secrets are intentionally absent from public binding and status DTOs.
- Service-level Web Push declaration through `IService.data.webPush`. Immutable deployment declarations can require the `pushnotification` platform capability alongside database and object-storage capabilities; this does not turn Web Push into a Corestore resource or volume capability.
- `IUser`, JWT-only `IIdentityCredential`, full `IIdentity`, and token-related contracts for authentication context. `IIdentity` extends `IIdentityCredential` with server-issued user metadata.
- `ICloudlyConfig`, `ICloudlySettings`, status, server, bare-metal, BaseOS, backup, and task execution interfaces for control-plane state.

Shared service port helpers are exported from `data` so Cloudly, App Store resolution, Coreflow, Coretraffic, Onebox, and dcrouter agree on the same normalization rules:

```typescript
const normalizedPorts = data.normalizeServicePortConfig(service.data);
const defaultTarget = data.resolveDefaultServiceTargetPort(normalizedPorts.targetPorts);
const webTarget = data.resolveServiceTargetPort(normalizedPorts.targetPorts, 'web');

if (webTarget && data.isHttpServiceTargetProtocol(webTarget.protocol)) {
  // safe to use as a domain route target
}
```

`normalizeServicePortData()` returns service data with canonical `targetPorts`, domain `targetPort` refs, and `publicPortMappings` while removing legacy domain `port` fields from normalized writes.

## Hosted-App Authorization and Platform OIDC

An App Store version can declare that it supports platform-managed OpenID Connect. This is a capability declaration only: Onebox and Cloudly keep OIDC disabled until an administrator explicitly enables it for that exact app instance, and they can disable it again without changing the template.

```typescript
import { appstore } from '@serve.zone/interfaces';

const config: appstore.IAppStoreVersionConfig = {
  image: 'registry.example.com/example/app:1.0.0',
  platformOidc: {
    redirectPath: '/auth/oidc/callback',
    roles: [
      { id: 'admin', label: 'Administrator' },
      { id: 'user', label: 'User' },
    ],
    environmentVariables: {
      issuerUrl: 'SERVEZONE_PLATFORM_OIDC_ISSUER',
      clientId: 'SERVEZONE_PLATFORM_OIDC_CLIENT_ID',
      clientSecret: 'SERVEZONE_PLATFORM_OIDC_CLIENT_SECRET',
      redirectUri: 'SERVEZONE_PLATFORM_OIDC_REDIRECT_URI',
      audience: 'SERVEZONE_PLATFORM_OIDC_AUDIENCE',
    },
    clientAuthenticationMethod: 'client_secret_basic',
  },
};
```

`redirectPath` is a canonical callback path on the app's HTTPS origin. Registration validation also requires that canonical app origin explicitly and rejects cross-origin, normalized, query-bearing, fragment-bearing, or duplicate callback URLs. The five environment values are environment-key names, not credentials embedded in the manifest. A host injects the generated client secret through launcher environment delivery and injects the other registration values only while OIDC is enabled.

`data.IHostedAppRoleAssignment` binds a stable user subject to an immutable `appInstanceId`. That same app instance is the OIDC `client_id`, the ID-token `aud`, and the `servezone_app_instance_id` claim. Tokens include only the assigned roles for that audience; `preferred_username` is display metadata and must not be treated as identity authority.

`requests.hostedapp` exports the shared authorization RPCs used by a host dashboard. `getHostedAppAccessConfiguration` returns human user summaries, role assignments, and hosted-app summaries. `setHostedAppRoleAssignment` returns the assignment or `null` when it is removed, while `setHostedAppPlatformOidc` returns the current registration state. `getHostedAppOidcAuthorization` returns the app and role summary for a pending request; `completeHostedAppOidcAuthorization` and `cancelHostedAppOidcAuthorization` return the redirect URL. All six requests require a full `data.IIdentity`.

Validate untrusted manifests, registrations, and claims with:

- `data.validateHostedAppRoleDefinitions`
- `data.validateHostedAppPlatformOidcRegistration`
- `data.validateHostedAppPlatformOidcClaims`
- `appstore.validateAppStorePlatformOidcCapability`
- `appstore.validateAppStoreVersionPlatformOidc`

Hosted lifecycle RPCs accept only `IIdentityCredential`. After JWT
verification, handlers validate `IHostedAppMachineClaims` and derive the exact
app instance and service from `servezone_app_instance_id` and
`servezone_service_id`; callers cannot submit those selectors. Bootstrap
actions are either a canonical same-origin `setupRoute` path or a nonsecret
`message`. Control tokens, usernames, passwords, and token URLs are not part of
the lifecycle contract. Each server action carries a CAS revision; completion
requires its exact ID, current revision, and `ready` status so a delayed request
cannot complete a replacement action.

## TypedRequest Contracts

Use `requests` when registering handlers with `@api.global/typedrequest` or when creating typed requests through a TypedSocket client.

```typescript
import { requests } from '@serve.zone/interfaces';

type GetClustersRequest = requests.cluster.IReq_Any_Cloudly_GetClusters;

const methodName: GetClustersRequest['method'] = 'getClusters';
```

Each request interface follows the same pattern:

```typescript
interface IExampleRequest {
  method: 'methodName';
  request: Record<string, unknown>;
  response: Record<string, unknown>;
}
```

`requests.config.IRequest_Any_Cloudly_GetClusterConfig` accepts `data.IIdentityCredential`, which contains only the JWT needed for server-side identity resolution. Username/password login and machine-token exchange responses continue to return the full `data.IIdentity`.

### Immutable Deployment Contracts

Cloudly deployment authority is expressed as an exact `data.IServiceDeploymentGrant`. A `service` grant applies to one owned existing service. An `organization-service-slot` grant reserves authority for one exact future service ID in an organization; it is not an organization-wide wildcard. `configureServiceDeploymentMachineUser` accepts this discriminated `grant` object instead of separate service and capability fields.

The immutable deployment workflow is:

1. Reserve the exact service, namespace, registry repository/tag, and route intent with `reserveServiceDeployment`.
2. Push the OCI index to the returned exact tag with the authenticated deployer identity.
3. Promote the authenticated release evidence with `promoteServiceImageRelease`.
4. Observe exact rollout and runtime-digest evidence with `getServiceDeploymentStatus`.
5. For a greenfield service, expose and verify its public route with `promoteServiceDeploymentRoute` only after the immutable rollout succeeds.

`data.IDeploymentRouteRequest.proxied` carries provider-specific DNS proxy intent as an optional boolean. New route declarations should set it explicitly; omission remains valid for persisted historical operations and legacy callers.

`data.IServiceDeploymentOperation` is the durable revisioned compare-and-set fence for this workflow. Image promotion requires Cloudly-created trusted evidence that binds the operation, actor, repository, exact tag, root digest, and OCI index media type. The service request group also exposes deployment preflight, exact-digest rollback, retry, and cleanup contracts.

Gateway request contracts include `getGatewayClientRoutes` (`requests.gateway.IReq_GetGatewayClientRoutes`) for listing owned `IGatewayClientRoute[]` route views, and `syncGatewayClientRoute` for idempotently syncing or deleting hostname-owned, routeRef-owned, and combined hostname-plus-routeRef routes. A client can label canonical intent with `managedRouteKind: 'letsencrypt-http01-forward'` and set a higher `priority` for a path-specific HTTP-01 route while retaining a separate normal route for the same hostname. Mail request contracts include `syncMailAddressBinding`, `deleteMailAddressBinding`, `rotateMailCredential`, and `getMailDeliveryStatus`. `IReq_GetMailDeliveryStatus` looks up a delivery spool item by `spoolItemId`, returns `data.IMailDeliveryStatus`, and accepts `IMailSubmissionRequestAuth` so service-mail credentials can query their own accepted, queued, deferred, delivered, or failed status. Typed outbound messages may set `replyTo` to one bare ASCII mailbox address; arbitrary `Reply-To` values do not belong in the custom header bag. Invalid values and typed-field/custom-header conflicts return stable `TMailSubmissionErrorCode` values. `TMailAddressBindingSync.outboundEnabled` explicitly controls whether a gateway should maintain a managed outbound SMTP credential for an address binding. Binding credential metadata is public; `rotateMailCredential` returns the new secret only in its one-time `IMailCredentialOneTimeSecret` response.

### Web Push Contracts

New Web Push integrations use `requests.webpush`. Control-plane methods and application delivery methods deliberately use different, non-overlapping authentication types:

- `listWebPushBindings`, `syncWebPushBinding`, `deleteWebPushBinding`, `rotateWebPushCredential`, and `rotateWebPushVapidKey` use control-plane identity or gateway API-token authentication.
- `getWebPushServiceStatus`, `enqueueWebPush`, `cancelWebPush`, and `getWebPushDeliveryStatus` require a Web Push application credential. The gateway derives the owner exclusively from that credential; application requests cannot submit owner identity.

`syncWebPushBinding` may return the initial application credential secret once, and `rotateWebPushCredential` may return its replacement once. Binding and status DTOs contain only public credential and VAPID metadata. `enqueueWebPush` requires a credential-scoped idempotency key, an opaque application subscription ID, the browser Push API subscription, the VAPID key ID used for that browser subscription, and a privacy-minimal `notificationAvailable` signal.

```typescript
import { requests } from '@serve.zone/interfaces';

type EnqueueWebPush = requests.webpush.IReq_EnqueueWebPush;
type WebPushStatus = requests.webpush.IReq_GetWebPushDeliveryStatus;
```

A delivery state of `pushServiceAccepted` means only that the remote push service accepted the encrypted request. It does not prove browser receipt, notification display, or user interaction.

### Gateway Client Lifecycle and DNS

`syncGatewayClientRoute` accepts an optional `dnsMode`. Omission means `skip` for older clients. `observe` reports DNS without changing it. `reconcile` makes the gateway authoritative for the exact route hostname: it claims or replaces manual A, AAAA, and CNAME records, including already-correct manual values. Its optional `dns` result contains a closed status, retryability, the desired A/AAAA target, overwritten-record evidence, `checkedAt`, and `authoritativeVerifiedAt` once the provider or authoritative server confirms the state. Consumers can carry that evidence while retrying public propagation instead of treating an immediate recursive lookup miss as permanent.

Use `requests.gateway.IReq_ProvisionGatewayClientCredential` to replace an admin/bootstrap token with a first-class gateway-client credential. The admin-authenticated request idempotently upserts a `data.IGatewayClient`, durably creates a new bound credential, returns its raw value once, and then revokes older credentials bound to that client. It never revokes the bootstrap/admin credential. A successful response is discriminated with `success: true` and always includes the action, durable client, one-time credential, and revocation count.

```typescript
import { requests } from '@serve.zone/interfaces';

const provisioning: requests.gateway.IReq_ProvisionGatewayClientCredential['request'] = {
  apiToken: 'admin-bootstrap-token',
  provisioning: {
    id: 'cloudly-main',
    type: 'cloudly',
    name: 'Cloudly main',
    hostnamePatterns: ['*'],
    allowedRouteTargets: [
      {
        host: 'coretraffic.internal',
        ports: [],
        allowAnyPort: true,
      },
    ],
    capabilities: {
      readDomains: true,
      readDnsRecords: true,
      readRoutes: true,
      syncRoutes: true,
      syncDnsRecords: true,
      readMail: true,
      manageMail: true,
      readCertificates: true,
      requestCertificates: true,
    },
  },
};
```

`getGatewayClientContext` returns effective live policy. A `gatewayClient` role necessarily includes the credential ID, bound client ID/type, and `policyGeneration`; consumers should reject admin/operator or mismatched contexts rather than falling back to a caller-supplied owner ID. `getGatewayClientMailOverview` provides an owner-scoped domain and recent-message summary. `getGatewayClientMailDomainCount` derives ownership exclusively from the authenticating gateway credential and returns `{ count: number }` for its distinct configured mail domains. Cloudly therefore uses only `dcrouterGatewayApiToken`; the former `dcrouterOpsApiToken` setting is not part of `data.ICloudlySettings`.

### CoreMail contracts

`requests.coremail` is the shared contract boundary for authenticated workload
sessions, Coreflow reconciliation, and the CoreMail-to-dcrouter gateway
session. Only the three authentication handshakes carry reusable peer
credentials. Subsequent transfer operations may carry a scoped, short-lived,
one-time bearer capability, while every subsequent workload request derives
tenant, service, binding, capabilities, allowed senders, and the composite
credential ID/version identity from the server-owned TypedSocket peer.
Successful workload authentication also returns the effective binding state and
the exact `allowedOperations` derived from
`data.coreMailWorkloadOperationPolicy`. Disabled bindings never authenticate;
draining bindings permit outbound status plus inbound list/fetch/ack only.

Gateway recipient resolution uses four strict outcomes. `accept`, `defer`, and
`reject` are authoritative only for recipients owned by an active binding.
`unhandled` means the CoreMail peer does not own that recipient, so the gateway
may continue its next configured resolver. Consumers must apply
`data.normalizeCoreMailRecipientResolutions()` against the exact requested
recipient set before acting on a peer response.

Large content never travels inside TypedRequest JSON. Outbound body parts and
attachments use prepare/upload/complete operations with short-lived one-time
HTTP transfer grants. Inbound delivery uses bounded delivery listing followed
by prepare/fetch/complete and an explicit acknowledgement after the workload
has processed the exact byte count and SHA-256 digest. `data.coreMailLimits`
defines the 64 KiB control-frame boundary, bounded structured content, the
30 MiB serialized MIME ceiling, a 56 KiB inbound page budget, bounded opaque
cursors, transfer deadlines, and five-minute grant lifetime.

Coreflow applies `data.ICoreMailDesiredState` with a config-epoch
compare-and-set fence. It stages the digest-fenced JSON snapshot through a
bounded one-time HTTP upload, then applies it by reconciliation ID, so a large
binding set never bypasses the 64 KiB control-frame boundary. Desired bindings
contain password verifiers and secret references only, never plaintext
workload or dcrouter credentials.
Use `data.normalizeCoreMailDesiredState`,
`data.canonicalizeCoreMailDesiredState`, and
`data.createCoreMailDesiredStateDigest` at every producer and consumer
boundary. Use `data.verifyCoreMailDesiredStateDigest` before applying a staged
snapshot, `data.normalizeCoreMailCredentialVerifier` before accepting verifier
metadata, `data.normalizeCoreMailControlBootstrap` for startup authority, and
`data.normalizeCoreMailGatewayPeerDesiredState` for dcrouter peer state. The
normalizers reject unknown fields, noncanonical mailboxes,
ambiguous active recipient ownership, malformed SHA-256 values, and
credential-lifecycle inconsistencies. CoreMail credential verifiers use the
versioned `argon2id-v1` format and the exact policy exported as
`data.coreMailCredentialVerifierPolicy`.

`data.ICoreMailControlBootstrap` is installed before ordinary reconciliation.
It contains the CoreMail service identity and verifier metadata only. The
matching plaintext control credential is delivered exclusively to Coreflow
through resolved runtime secrets. `data.coreMailRuntimeKeys` publishes the
canonical environment keys for the verifier-only bootstrap payload and the
separate control and gateway secret values; no consumer may derive or embed
plaintext material in desired state.

CoreMail schema v2 keeps durable authority at the stable tenant, service, and
binding identity while revisions, config epochs, and composite
`credentialId`/`version` values fence sessions and new actions. Credential
versions are authority-wide monotonic and unique even when rotation changes the
credential ID. `active`
bindings accept new mail, `draining` bindings permit existing status and
inbound fetch/ack work without accepting new mail, and `disabled` bindings
reject authentication. Cloudly retains a draining binding until pending
inbound delivery reaches zero as reported by
`ICoreMailBindingReconciliationStatus.pendingInboundCount`.

`data.ICoreMailGatewayPeerDesiredState` gives dcrouter the authoritative HTTPS
CoreMail transfer origin associated with an authenticated CoreMail service.
The same origin is carried in CoreMail desired state and returned by workload
and gateway authentication. It must never be inferred from a socket, Host
header, TypedSocket tag, or unrestricted peer input. CoreMail compares both
authoritative views before handing over a path-only transfer grant.

Inbound delivery pagination uses an opaque CoreMail-owned `cursor`, is bounded
by `data.coreMailLimits.inboundPageSize`, and returns `nextCursor` only when
another page may exist. Consumers must not construct or parse cursor contents.
Cursor signing keys are value-free runtime references with one current and bounded
retiring versions; plaintext remains in resolved runtime secrets.

Quota windows are fixed UTC minute/day buckets. A new outbound quota unit is
consumed only by the first durable insertion of an idempotency identity, while
replays consume none. Pending inbound includes every state except
`acknowledged`. These semantics are exported as `data.coreMailQuotaPolicy`.
Every binding carries finite `messagesPerMinute`, `messagesPerDay`, and
`maxPendingInbound` values; omitted or unlimited quotas are not valid schema-v2
desired state.
`data.coreMailRetentionPolicy` retains terminal outbound,
acknowledged inbound, and idempotency receipts for 30 days and expired
capabilities for 24 hours. Pending inbound is never age-purged.

HTTP transfers use canonical `/transfers/<uuid>` paths and one-time Bearer
tokens. `data.coreMailTransferTokenPolicy` requires canonical 256-bit
base64url token material, while `issuedAt` and `expiresAt` prove the exact
five-minute lifetime. PUT succeeds with 204 and GET with 200. Content length
and type must match the grant; digest integrity is bound by grant metadata and repeated in
the completion RPC rather than an optional HTTP digest header. Apply the
exported strict normalizers for outbound message descriptors, envelopes,
method-specific upload/download grants, submissions, gateway outbound
statuses, inbound deliveries,
desired state, bootstrap state, and gateway-peer state at their corresponding
untrusted request and response boundaries. Normalizer failures throw
`data.CoreMailContractError`; its readonly `code` defaults to
`INVALID_REQUEST`, while an otherwise valid outbound part that exceeds its
kind-specific byte budget reports `PAYLOAD_LIMIT_EXCEEDED`.

Control and gateway credential rotation is ordered: provision the candidate
plaintext through resolved runtime secrets, publish and activate the matching
`argon2id-v1` verifier, roll or reconnect every affected replica, observe
per-task authentication and readiness, then mark the previous verifier
`retiring` with `acceptUntil`. Remove the previous verifier and secret only
after its acceptance window has elapsed and no session uses that composite
credential identity.
Reconciliation status carries the exact CoreMail task, rollout generation, and
image digest so Coreflow can correlate every response with its authoritative
current task roster. Apply `data.normalizeCoreMailReconciliationStatus` before
using pending-inbound or composite active-session counts for drain and rotation
decisions.

Request groups are exported by product area:

- `requests.admin`
- `requests.appstore`
- `requests.baremetal`
- `requests.baseos`
- `requests.backup`
- `requests.certificate`
- `requests.cluster`
- `requests.config`
- `requests.coremail`
- `requests.corestore`
- `requests.deployment`
- `requests.dns`
- `requests.domain`
- `requests.externalRegistry`
- `requests.gateway`
- `requests.hostedapp`
- `requests.identity`
- `requests.image`
- `requests.inform`
- `requests.log`
- `requests.mail`
- `requests.migration`
- `requests.network`
- `requests.node`
- `requests.platform`
- `requests.routing`
- `requests.secret`
- `requests.server`
- `requests.service`
- `requests.settings`
- `requests.status`
- `requests.task`
- `requests.version`
- `requests.webpush`

Secret material response contracts are not exported from the universal
browser-facing entrypoint.

The root entrypoint does use `@push.rocks/smartcrypto` to parse and validate
strict ingress envelopes. Browser consumers that import the root contract can
therefore include SmartCrypto and its browser-compatible crypto dependencies in
their bundle. This package never opens private keys; sealed runtime material
contracts remain isolated to the Node-only `/runtime` subpath.

Node runtimes import the isolated subpath:

```typescript
import {
  verifySealedResolvedSecretMaterial,
} from '@serve.zone/interfaces/runtime';
import type {
  IReq_GetResolvedSecretMaterial,
  ISecretMaterialExpectation,
  ISealedResolvedSecretMaterial,
} from '@serve.zone/interfaces/runtime';

async function acceptMaterial(
  material: ISealedResolvedSecretMaterial,
  expectation: ISecretMaterialExpectation,
) {
  if (!await verifySealedResolvedSecretMaterial(material, expectation)) {
    throw new Error('secret material does not match its immutable manifest');
  }
}
```

Runtime material is shaped as `{ schemaVersion: 1, manifest, entries }`, where
each entry contains only `secretVersionId` and a strict X25519 envelope. The
helper verifies the canonical schema-v2 manifest digest, envelope context
digests, active recipient key, sorted exact one-to-one version coverage, every
request fence, and the trusted local organization/cluster expectation. It never
decrypts. Organization and cluster are derived from the verified cluster JWT
and cannot be selected by request fields.

### Corestore Runtime Credentials

The Node-only `/runtime` export defines
`getCorestoreControlCredentialMaterial` for recipient-bound sealed retrieval and
`publishCorestoreCredentialMaterial` for ingress-sealed database or object
storage credential publication. `isCorestoreControlToken()` validates the
canonical token boundary, while
`createCorestoreControlCredentialPlaintextBytes()` emits its exact JSON
plaintext directly from validated UTF-8 bytes without first constructing a
JavaScript token string. Publication grants carry the service, binding,
reconciliation generation, binding-request digest, target Secrets revision, and
bounded validity window. `computeCorestoreCredentialBindingRequestSha256()`
produces that digest from the exact value-free provider, scope, environment, and
optional retention request for persisted schema-v1 records. New issuers use
`TCorestoreCredentialBindingRequestV2` and
`validateCorestoreCredentialBindingRequestV2()`,
`createCorestoreCredentialBindingRequestDigestInputV2()`, and
`computeCorestoreCredentialBindingRequestSha256V2()`. The corresponding
unsuffixed APIs remain the schema-v1 boundary. Schema v2 binds every object-storage
request to the exact durable `bucketName`; schema-v1 records stay readable but
do not provide that authority. Object-storage plaintext keeps the logical
`accessKeyId` and `secretAccessKey` fields while
`createCorestoreCredentialSecretValues()` expands them to the six canonical
Corestore `S3_*` and `AWS_*` Secret aliases. Receipts bind that exact key
coverage, created
SecretVersion references, the admitted envelope/context digest, and the trusted
organization and cluster scope. Cluster configuration DTOs add optional sorted
`corestoreCredentialPublicationGrants`; existing consumers may omit them.
When `validateCorestoreObjectStorageCredentialMaterial()` receives a trusted
retention expectation, matching retention evidence is mandatory; missing or
different evidence fails validation.

The runtime export also defines exact Corestore database backup receipts,
allocation references, restore requests, and restore responses. Use
`normalizeCorestoreDatabaseAllocationReference()`,
`encodeCorestoreDatabaseAllocationReference()`, and
`computeCorestoreDatabaseAllocationReferenceSha256()` for the allocation
boundary; the receipt, restore-request, and restore-response APIs follow the
same exact normalize, encode, and SHA-256 naming. They enforce bounded canonical
JSON without performing a backup or restore. A restore request may carry
`expectedDatabaseAllocation` so the restore implementation can fence the exact
scratch allocation, and may carry the caller-owned `restoreAttemptId` to select
a new fenced idempotency attempt. The attempt ID uses the database-backup
identifier grammar and 256-character maximum. Omitting the snapshot
`databaseAllocation` field together with request-level
`expectedDatabaseAllocation` and `restoreAttemptId` preserves legacy request
bytes and digests exactly.
`corestoreDatabaseBackupRuntimeLimits` publishes the distinct 96 MiB snapshot
payload, one-byte-larger framed snapshot-original, 128 MiB verified closure
plaintext, and 256 MiB closure boundaries used by Corestore.

### Legacy Deployment Settlement

The Node-only `/runtime` export also provides strict, exact-schema codecs for
Cloudly legacy deployment settlement plans, production requests, check/apply
summaries, stable outcomes, and scratch/production receipts. Authenticated
artifacts use domain-separated HMAC inputs and references derived from the full
authority-metadata SHA-256; authority-aware normalizers enforce metadata fences
and artifact-time validity windows. General authority-aware APIs require a
consumer-owned, constant-time HMAC verifier; private stdio authority-aware APIs
derive verification from their exact plaintext authority handoffs. They never
read the current wall clock.
Structural normalizers explicitly do not authenticate MACs. Literal portable
golden vectors contain only safe
metadata, summaries, digests, and MACs. The separately named
`ICloudlyLegacyDeploymentSettlementAuthorityHandoffPlaintextV1` parser/encoder
exists only for private maintenance-container interoperability and is never part
of a safe result, receipt, or golden summary.

Fenced settlement contracts add deterministic execution-attempt identities and
prepared, committed, or aborted observations. Resolution bundles distinguish
`committed-schema1`, `committed-schema2`, `aborted-prepared`,
`observed-aborted`, `absent-tombstone`, and `legacy-no-op`; only the scratch-only
legacy no-op has no durable attempt observation. Source and execution authority
status are bound to the resolution's `observedAt`. Scratch attempts use the same
authority for both roles and require matching statuses. Historical verification
remains possible after expiry because status is derived only from the
authenticated observation time. Production recovery always uses receipt v2 and
a distinct execution authority B whose metadata names source authority A and
binds the authenticated plan, request bundle, and scratch receipt digests.
B is issued strictly after scratch completion; B's `requestedAt` and
`approvedAt` are independent of A's request timestamp and must remain within
B's own validity interval.

Consumers make trust decisions through
`normalizeAuthenticatedCloudlyLegacyDeploymentSettlementResolutionForAuthorities()`,
`normalizeCloudlyLegacyDeploymentSettlementScratchExecutionBundleForAuthority()`,
`normalizeCloudlyLegacyDeploymentSettlementProductionExecutionBundleV2ForAuthorities()`,
`normalizeCloudlyLegacyDeploymentSettlementStdioRequestForAuthorities()`, and
`normalizeCloudlyLegacyDeploymentSettlementStdioResponseForRequestAuthorities()`.
Every resolution outcome, including an unapplied outcome, is carried in an
`IAuthenticatedCloudlyLegacyDeploymentSettlementResolutionV1` recovery
attestation under the dedicated resolution HMAC domain and signed by the
execution authority. The raw
`normalizeCloudlyLegacyDeploymentSettlementResolutionBundleForAuthorities()`
helper verifies referenced artifacts and status only; its result is not an
authenticated recovery claim. The non-stdio authority-aware APIs use the
caller-supplied HMAC verifier and never receive plaintext keys. The private
stdio variants accept no external verifier: they bind each HMAC to the
authority reference and key in the corresponding request handoff. Interfaces
never executes a settlement.

The stdio contract exposes nine commands from `settlement-plan` through
`settlement-production-resolve`. Requests and responses are exact canonical
JSON followed by one LF, bounded by
`cloudlyLegacyDeploymentSettlementStdioLimits`, and responses use a
command-discriminated success payload or a finite secret-safe error. Private
authority handoffs and MongoDB descriptors belong only on protected request
transport. Production stdio requests reject equal decoded A and B HMAC keys and
wipe temporary comparison buffers. The handoff-derived verifier uses HMAC-SHA256
with constant-time MAC comparison, rejects wrong or swapped handoff keys, and
wipes temporary decoded key and MAC buffers. Handoffs and derived verifiers are
private in-process capabilities that must never be persisted or logged and must
be discarded when the stdio exchange ends. Verifier-only integrations do not
expose key material to Interfaces, so their callers must enforce the same key
separation.
Authority-aware response validation also binds settlement-request
`requestedAt`, production-authorization `requestedAt` and `approvedAt`, and
resolution `observedAt` to the corresponding request. Production check and
apply results must be issued by B no earlier than both A's request and B's
`approvedAt`. Callers remain responsible for process isolation, key
destruction, logging discipline, database access, and the actual settlement
transaction.

Launcher environment delivery uses stable `szsv-<base32-sha256>` Docker
resource names and `/run/serve.zone/secrets/<resource>` source paths. The
nonsecret map is written to `/run/serve.zone/workloadinit-map-v1.json` with mode
0444. Runtime assets under `/opt/serve.zone/runtime-assets` are read-only, and
the wrapper executes
`workloadinit run --map /run/serve.zone/workloadinit-map-v1.json -- <argv...>`
without a shell, symlink, or aggregate value file. Resources and maps remain
retained while any desired, applied, or previous-accepted manifest references
them and are deleted only after Docker confirms rollout or service removal.

## Platform Contracts

Use `platform` for current platform-service capabilities and application-facing platform RPCs.

```typescript
import { platform } from '@serve.zone/interfaces';

type SendEmailRequest = platform.email.IReq_SendEmail;
type PlatformBinding = platform.IPlatformBinding;

const sendEmailMethod: SendEmailRequest['method'] = 'sendEmail';
```

Available platform modules:

- `platform.email` for transactional email, recipient registration, email status, and email stats.
- `platform.sms` for SMS delivery and verification-code delivery.
- `platform.pushnotification` is the deprecated legacy device-token push contract. New browser Web Push integrations use `requests.webpush`.
- `platform.letter` for physical letter workflows.
- `platform.ai`, `platform.database`, `platform.objectstorage`, `platform.logging`, `platform.backup`, and `platform.sip` for infrastructure and application capabilities.
- `platform.storage` for provider-neutral storage classes, requests, capabilities, and resolved bindings.
- `platform.objectstorageretention` for value-free immutable-retention intent,
  capability and sentinel evidence, authority binding, digest helpers, and
  strict validators.
- `platform.storagemigration` for fenced object-storage migration intent, status, consumer acknowledgements, canonical digests, and strict normalizers.
- `platform.types` provider and binding metadata is value-free. Provider-specific
  operational config stays adapter-internal, while public DTOs expose typed
  endpoints and optional credential management scopes only.

Optional `IPlatformBinding.objectstorageBucketName` is the durable value-free
Cloudly authority for an exact Corestore bucket. It is required whenever a
caller needs to establish trusted bucket authority for retention validation.
Cluster runtimes report the exact provider-returned bucket through the optional
`requests.platform.IReq_Any_Cloudly_UpdatePlatformBindingStatus.request.objectstorageBucketName`
field when updating binding status.
`validatePlatformObjectStorageBucketName()` applies the canonical SmartStorage
S3 bucket boundary without normalizing input, and
`platformObjectStorageBucketNameLimits` publishes its 3-byte minimum and 63-byte
maximum.
`IPlatformBinding.objectstorageRetention` carries compliance-mode intent and
optional provider evidence bound to the exact service, binding, reconciliation
generation, request digest, and authoritative bucket. Use
`validatePlatformBindingObjectStorageRetention()` for the full binding boundary
or the narrower intent/evidence validators when the trusted authority context
is already available; none of these contracts contains credential material.

## Legacy Platformservice Contracts

`platformservice` is retained for older integrations that still consume the previous namespace layout.

```typescript
import { platformservice } from '@serve.zone/interfaces';

type LegacySendEmailRequest = platformservice.mta.IRequest_SendEmail;
```

New code should prefer `platform` unless it must remain compatible with an active legacy consumer.

## Contract Ownership

Only ecosystem-wide public contracts belong in this package. Cloudly-internal implementation details, service-private DTOs, and temporary migration helpers should stay in their owning service until they become real shared contracts.

Good candidates for this package:

- Types persisted or exchanged across multiple serve.zone services.
- TypedRequest contracts used by more than one project.
- SDK-facing interfaces that external consumers should be able to rely on.

Poor candidates for this package:

- Private implementation details of one service.
- Runtime helpers or convenience wrappers, unless they are shared contract normalization or validation helpers used by multiple packages.
- Compatibility aliases without an active consumer.

## Development

```bash
pnpm install
pnpm run build
pnpm test
pnpm run buildDocs
```

The package is authored as ESM TypeScript and built with `tsbuild tsfolders`.

## License and Legal Information

This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [license](./license) file.

**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.

### Trademarks

This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.

Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.

### Company Information

Task Venture Capital GmbH<br>
Registered at District Court Bremen HRB 35230 HB, Germany

For any legal inquiries or further information, please contact us via email at hello@task.vc.

By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
