All files / service-discovery-types/src contact.ts

100% Statements 4/4
100% Branches 0/0
100% Functions 0/0
100% Lines 4/4

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176                                                                                                                1x                                             1x                                                                                                                                                                   1x               1x            
/**
 * Contact-point and contact-initiation interfaces.
 *
 * Contact points are remotable objects, not JSON-serializable data, so they
 * are typed in TypeScript but not validated at the wire level.
 */
 
import { array, exactOptional, object, string } from '@metamask/superstruct';
import type { Infer, Struct } from '@metamask/superstruct';
 
import { ServiceDescriptionStruct } from './service-description.ts';
import type { ServiceDescription } from './service-description.ts';
 
/**
 * The service API as seen by the consumer: the concrete method interface
 * through which service requests are made. The actual shape is
 * service-specific and is fully described by `ServiceDescription.apiSpec`.
 * This type serves as a nominal bound for typed references.
 */
export type ServicePoint = {
  [method: string]: (...args: unknown[]) => Promise<unknown>;
};
 
/**
 * An authentication/authorization credential. Shape is access-model
 * specific, so it is left as `unknown` at this layer.
 */
export type Credential = unknown;
 
/**
 * Endpoint for submitting credentials in a permissioned contact flow. On
 * success, returns a usable service endpoint.
 */
export type CredentialSubmissionPoint = {
  submit(credential: Credential): Promise<ServicePoint>;
};
 
/**
 * Permissioned access-model response: a credential spec plus an ocap to
 * submit credentials to. On successful submission the consumer receives a
 * service endpoint.
 */
export type PermissionedContactResponse = {
  kind: 'permissioned';
  credentialSpec: string;
  credentialSubmissionPoint: CredentialSubmissionPoint;
};
 
/**
 * Description of a required code-bundle certification.
 */
export type CertificationRequirement = {
  description?: string;
  acceptedCertificationKinds: string[];
};
 
export const CertificationRequirementStruct: Struct<CertificationRequirement> =
  object({
    description: exactOptional(string()),
    acceptedCertificationKinds: array(string()),
  });
 
/**
 * A serializable bundle of code suitable for loading into a vat.
 */
export type VatCodeBundle = {
  payload: unknown;
};
 
/**
 * A certificate attesting to some property of a code bundle, issued by a
 * body whose judgment the provider trusts.
 */
export type ValidationCertificate = {
  issuer: string;
  attestations: string[];
  signature: string;
};
 
export const ValidationCertificateStruct: Struct<ValidationCertificate> =
  object({
    issuer: string(),
    attestations: array(string()),
    signature: string(),
  });
 
/**
 * Endpoint for submitting a validated-client code bundle. The provider
 * performs whatever verification it desires (certificate checks, static
 * analysis, human review, etc.) and, on success, launches a vat containing
 * the bundle endowed with the service endpoint. Returns a reference to the
 * vat root or a designated ocap within it.
 */
export type CodeSubmissionPoint = {
  submit(
    bundle: VatCodeBundle,
    certificates?: ValidationCertificate[],
  ): Promise<unknown>;
};
 
/**
 * Validated-client access-model response: the required certifications and an
 * ocap for submitting a code bundle.
 */
export type ValidatedClientContactResponse = {
  kind: 'validatedClient';
  requiredCertifications: CertificationRequirement[];
  codeSubmissionPoint: CodeSubmissionPoint;
};
 
/**
 * Public access-model response: the provider returns a service endpoint
 * directly, wrapped so the response shape is uniformly tagged.
 */
export type PublicContactResponse = {
  kind: 'public';
  service: ServicePoint;
};
 
/**
 * The response returned by `ContactPoint.initiateContact()`. Tagged with
 * a `kind` discriminator so consumers can `switch (response.kind)`
 * exhaustively rather than structurally probing (`'credentialSpec' in
 * response`). Without the tag, a `ServicePoint` whose API happened to
 * include a method named `credentialSpec` or `requiredCertifications`
 * would be misclassified.
 */
export type ContactResponse =
  | PublicContactResponse
  | PermissionedContactResponse
  | ValidatedClientContactResponse;
 
/**
 * The contact protocol: the uniform interface implemented by every contact
 * endpoint, regardless of the underlying service.
 */
export type ContactPoint = {
  /** Obtain the service description for this service. */
  getServiceDescription(): Promise<ServiceDescription>;
 
  /**
   * Callback for the service matcher to validate that the registration of
   * this service was legitimate. The matcher supplies the token it received
   * in the `register*` call; the provider verifies the token matches a
   * token it previously issued.
   */
  confirmServiceRegistration(registrationToken: string): Promise<void>;
 
  /**
   * Initiate contact with the service. The shape of the response depends on
   * the service's access model (see {@link ContactResponse}).
   */
  initiateContact(): Promise<ContactResponse>;
};
 
/**
 * A registration token generated by the provider and exchanged with the
 * matcher. Its value is opaque to the matcher; the provider uses it to
 * authenticate the callback from the matcher during
 * `confirmServiceRegistration`.
 */
export const RegistrationTokenStruct: Struct<string> = string();
 
export type RegistrationToken = Infer<typeof RegistrationTokenStruct>;
 
/**
 * Shape used when a provider registers a service directly by value
 * (not via URL or ocap reference).
 */
export const RegistrationEnvelopeStruct = object({
  description: ServiceDescriptionStruct,
  registrationToken: RegistrationTokenStruct,
});
 
export type RegistrationEnvelope = Infer<typeof RegistrationEnvelopeStruct>;