componentized/directory

componentized:services

interface

Interfaces

  • types
  • credential-store โ€” Credential Stores allow a client to fetch credentials for a specific binding. The platform typically implements this interface keeping the implementation details of credential storage hidden from the service lifecycle and users.
  • credential-admin โ€” Credential Admins manage the credentials for a binding. The platform typically implements this interface keeping the implementation details of credential storage hidden from the service lifecycle and users.
  • lifecycle โ€” Service lifecycle manage a specific type of service on demand. Allowed tiers and requested attributes are defined by the specific implementation.

Worlds

WIT

package componentized:services;

interface types {
  type service-instance-id = string;

  type service-binding-id = string;

  record credential {
    key: string,
    value: string,
  }

  record request {
    key: string,
    value: string,
  }

  type scope = string;

  type tier = string;

  type error = string;
}

/// Credential Stores allow a client to fetch credentials for a specific binding. The platform
/// typically implements this interface keeping the implementation details of credential storage
/// hidden from the service lifecycle and users.
interface credential-store {
  use types.{service-binding-id, credential, error};

  /// Fetch credentials from the store for a specific binding.
  fetch: func(binding-id: service-binding-id) -> result<list<credential>, error>;
}

/// Credential Admins manage the credentials for a binding. The platform typically implements this
/// interface keeping the implementation details of credential storage hidden from the service
/// lifecycle and users.
///
/// Some clients are not able to support rotating credentials. For those clients, creating a new
/// binding is recommended. Once the previous credentials are no longer in use, the old binding can
/// be unbound.
interface credential-admin {
  use types.{service-binding-id, credential, error};

  /// Publish new or refreshed credentials to the store for use by the service binding. When
  /// rotating credentials, the previously valid credentials should be revoked after calling
  /// publish with the new credentials.
  publish: func(binding-id: service-binding-id, credentials: list<credential>) -> result<_, error>;

  /// Destroy credentials previously published for a binding. Valid credentials should be revoked
  /// before calling destroy.
  destroy: func(binding-id: service-binding-id) -> result<_, error>;
}

/// Service lifecycle manage a specific type of service on demand. Allowed tiers and requested
/// attributes are defined by the specific implementation.
interface lifecycle {
  use types.{service-instance-id, service-binding-id, request, scope, tier, error};

  /// Provision a new service instance based on the service type, tier and requested attributes.
  /// A lifecycle may be able to provision one or more types of services. If the service type
  /// cannot be provisioned by this lifecycle, an error is returned. A service-error is returned
  /// if the tier or attributes are invalid, or if the service could not be provisioned for any
  /// reason. A service-instance-id is returned on success which can be used to update the
  ///  service instance, create bindings, or destroy the service instance.
  provision: func(instance-id: service-instance-id, %type: string, tier: option<tier>, requests: option<list<request>>) -> result<_, error>;

  /// Update tier or requested attributes for an existing service. An error is returned
  /// if the tier or attributes are invalid, or if the service could not be updated for any
  /// reason.
  update: func(instance-id: service-instance-id, tier: option<tier>, requests: option<list<request>>) -> result<_, error>;

  /// Destroys an existing service, optionally requesting that state contained within the service
  /// be retained rather than destroyed. A service-error is returned if the retention request
  /// cannot be satisfied, or if the service could not be destroyed for any reason.
  destroy: func(instance-id: service-instance-id, retain: option<bool>) -> result<_, error>;

  /// Bind new credentials for the provisioned service publishing them to the credential store.
  /// Scopes limit access to the service for the binding. The specific scopes available are
  /// defined by the service. An error is returned if the scopes are invalid or the binding could
  /// not be created for any reason.
  bind: func(binding-id: service-binding-id, instance-id: service-instance-id, scopes: option<list<scope>>) -> result<_, error>;

  /// List bindings for a service instance.
  list-bindings: func(instance-id: service-instance-id) -> result<list<service-binding-id>, error>;

  /// Unbind removes the binding. Expire any valid credentials and remove them from the
  /// credential store. An error is returned if the binding could not be released for any reason.
  unbind: func(binding-id: service-binding-id, instance-id: service-instance-id) -> result<_, error>;
}

world service-lifecycle {
  import types;
  import credential-admin;
  import wasi:io/poll@0.2.3;
  import wasi:clocks/monotonic-clock@0.2.3;
  import wasi:clocks/wall-clock@0.2.3;
  import wasi:config/store@0.2.0-draft;
  import wasi:io/error@0.2.3;
  import wasi:io/streams@0.2.3;
  import wasi:filesystem/types@0.2.3;
  import wasi:filesystem/preopens@0.2.3;
  import wasi:logging/logging@0.1.0-draft;
  import wasi:random/random@0.2.3;
  import wasi:random/insecure@0.2.3;
  import wasi:random/insecure-seed@0.2.3;
  import wasi:sockets/network@0.2.3;
  import wasi:sockets/instance-network@0.2.3;
  import wasi:sockets/udp@0.2.3;
  import wasi:sockets/udp-create-socket@0.2.3;
  import wasi:sockets/tcp@0.2.3;
  import wasi:sockets/tcp-create-socket@0.2.3;
  import wasi:sockets/ip-name-lookup@0.2.3;

  export lifecycle;
}
world imports {
  import types;
  import credential-admin;
  import credential-store;
  import lifecycle;
}
package wasi:io@0.2.3 {
  interface poll {
    resource pollable {
      ready: func() -> bool;
      block: func();
    }

    poll: func(in: list<borrow<pollable>>) -> list<u32>;
  }
  interface error {
    resource error {
      to-debug-string: func() -> string;
    }
  }
  interface streams {
    use error.{error};
    use poll.{pollable};

    variant stream-error {
      last-operation-failed(error),
      closed,
    }

    resource input-stream {
      read: func(len: u64) -> result<list<u8>, stream-error>;
      blocking-read: func(len: u64) -> result<list<u8>, stream-error>;
      skip: func(len: u64) -> result<u64, stream-error>;
      blocking-skip: func(len: u64) -> result<u64, stream-error>;
      subscribe: func() -> pollable;
    }

    resource output-stream {
      check-write: func() -> result<u64, stream-error>;
      write: func(contents: list<u8>) -> result<_, stream-error>;
      blocking-write-and-flush: func(contents: list<u8>) -> result<_, stream-error>;
      flush: func() -> result<_, stream-error>;
      blocking-flush: func() -> result<_, stream-error>;
      subscribe: func() -> pollable;
      write-zeroes: func(len: u64) -> result<_, stream-error>;
      blocking-write-zeroes-and-flush: func(len: u64) -> result<_, stream-error>;
      splice: func(src: borrow<input-stream>, len: u64) -> result<u64, stream-error>;
      blocking-splice: func(src: borrow<input-stream>, len: u64) -> result<u64, stream-error>;
    }
  }
}


package wasi:clocks@0.2.3 {
  interface monotonic-clock {
    use wasi:io/poll@0.2.3.{pollable};

    type instant = u64;

    type duration = u64;

    now: func() -> instant;

    resolution: func() -> duration;

    subscribe-instant: func(when: instant) -> pollable;

    subscribe-duration: func(when: duration) -> pollable;
  }
  interface wall-clock {
    record datetime {
      seconds: u64,
      nanoseconds: u32,
    }

    now: func() -> datetime;

    resolution: func() -> datetime;
  }
}


package wasi:config@0.2.0-draft {
  interface store {
    variant error {
      upstream(string),
      io(string),
    }

    get: func(key: string) -> result<option<string>, error>;

    get-all: func() -> result<list<tuple<string, string>>, error>;
  }
}


package wasi:filesystem@0.2.3 {
  interface types {
    use wasi:io/streams@0.2.3.{input-stream, output-stream, error};
    use wasi:clocks/wall-clock@0.2.3.{datetime};

    type filesize = u64;

    enum descriptor-type {
      unknown,
      block-device,
      character-device,
      directory,
      fifo,
      symbolic-link,
      regular-file,
      socket,
    }

    flags descriptor-flags {
      read,
      write,
      file-integrity-sync,
      data-integrity-sync,
      requested-write-sync,
      mutate-directory,
    }

    flags path-flags {
      symlink-follow,
    }

    flags open-flags {
      create,
      directory,
      exclusive,
      truncate,
    }

    type link-count = u64;

    record descriptor-stat {
      %type: descriptor-type,
      link-count: link-count,
      size: filesize,
      data-access-timestamp: option<datetime>,
      data-modification-timestamp: option<datetime>,
      status-change-timestamp: option<datetime>,
    }

    variant new-timestamp {
      no-change,
      now,
      timestamp(datetime),
    }

    record directory-entry {
      %type: descriptor-type,
      name: string,
    }

    enum error-code {
      access,
      would-block,
      already,
      bad-descriptor,
      busy,
      deadlock,
      quota,
      exist,
      file-too-large,
      illegal-byte-sequence,
      in-progress,
      interrupted,
      invalid,
      io,
      is-directory,
      loop,
      too-many-links,
      message-size,
      name-too-long,
      no-device,
      no-entry,
      no-lock,
      insufficient-memory,
      insufficient-space,
      not-directory,
      not-empty,
      not-recoverable,
      unsupported,
      no-tty,
      no-such-device,
      overflow,
      not-permitted,
      pipe,
      read-only,
      invalid-seek,
      text-file-busy,
      cross-device,
    }

    enum advice {
      normal,
      sequential,
      random,
      will-need,
      dont-need,
      no-reuse,
    }

    record metadata-hash-value {
      lower: u64,
      upper: u64,
    }

    resource descriptor {
      read-via-stream: func(offset: filesize) -> result<input-stream, error-code>;
      write-via-stream: func(offset: filesize) -> result<output-stream, error-code>;
      append-via-stream: func() -> result<output-stream, error-code>;
      advise: func(offset: filesize, length: filesize, advice: advice) -> result<_, error-code>;
      sync-data: func() -> result<_, error-code>;
      get-flags: func() -> result<descriptor-flags, error-code>;
      get-type: func() -> result<descriptor-type, error-code>;
      set-size: func(size: filesize) -> result<_, error-code>;
      set-times: func(data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>;
      read: func(length: filesize, offset: filesize) -> result<tuple<list<u8>, bool>, error-code>;
      write: func(buffer: list<u8>, offset: filesize) -> result<filesize, error-code>;
      read-directory: func() -> result<directory-entry-stream, error-code>;
      sync: func() -> result<_, error-code>;
      create-directory-at: func(path: string) -> result<_, error-code>;
      stat: func() -> result<descriptor-stat, error-code>;
      stat-at: func(path-flags: path-flags, path: string) -> result<descriptor-stat, error-code>;
      set-times-at: func(path-flags: path-flags, path: string, data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>;
      link-at: func(old-path-flags: path-flags, old-path: string, new-descriptor: borrow<descriptor>, new-path: string) -> result<_, error-code>;
      open-at: func(path-flags: path-flags, path: string, open-flags: open-flags, %flags: descriptor-flags) -> result<descriptor, error-code>;
      readlink-at: func(path: string) -> result<string, error-code>;
      remove-directory-at: func(path: string) -> result<_, error-code>;
      rename-at: func(old-path: string, new-descriptor: borrow<descriptor>, new-path: string) -> result<_, error-code>;
      symlink-at: func(old-path: string, new-path: string) -> result<_, error-code>;
      unlink-file-at: func(path: string) -> result<_, error-code>;
      is-same-object: func(other: borrow<descriptor>) -> bool;
      metadata-hash: func() -> result<metadata-hash-value, error-code>;
      metadata-hash-at: func(path-flags: path-flags, path: string) -> result<metadata-hash-value, error-code>;
    }

    resource directory-entry-stream {
      read-directory-entry: func() -> result<option<directory-entry>, error-code>;
    }

    filesystem-error-code: func(err: borrow<error>) -> option<error-code>;
  }
  interface preopens {
    use types.{descriptor};

    get-directories: func() -> list<tuple<descriptor, string>>;
  }
}


package wasi:logging@0.1.0-draft {
  interface logging {
    enum level {
      trace,
      debug,
      info,
      warn,
      error,
      critical,
    }

    log: func(level: level, context: string, message: string);
  }
}


package wasi:random@0.2.3 {
  interface random {
    get-random-bytes: func(len: u64) -> list<u8>;

    get-random-u64: func() -> u64;
  }
  interface insecure {
    get-insecure-random-bytes: func(len: u64) -> list<u8>;

    get-insecure-random-u64: func() -> u64;
  }
  interface insecure-seed {
    insecure-seed: func() -> tuple<u64, u64>;
  }
}


package wasi:sockets@0.2.3 {
  interface network {
    resource network;

    enum error-code {
      unknown,
      access-denied,
      not-supported,
      invalid-argument,
      out-of-memory,
      timeout,
      concurrency-conflict,
      not-in-progress,
      would-block,
      invalid-state,
      new-socket-limit,
      address-not-bindable,
      address-in-use,
      remote-unreachable,
      connection-refused,
      connection-reset,
      connection-aborted,
      datagram-too-large,
      name-unresolvable,
      temporary-resolver-failure,
      permanent-resolver-failure,
    }

    enum ip-address-family {
      ipv4,
      ipv6,
    }

    type ipv4-address = tuple<u8, u8, u8, u8>;

    type ipv6-address = tuple<u16, u16, u16, u16, u16, u16, u16, u16>;

    variant ip-address {
      ipv4(ipv4-address),
      ipv6(ipv6-address),
    }

    record ipv4-socket-address {
      port: u16,
      address: ipv4-address,
    }

    record ipv6-socket-address {
      port: u16,
      flow-info: u32,
      address: ipv6-address,
      scope-id: u32,
    }

    variant ip-socket-address {
      ipv4(ipv4-socket-address),
      ipv6(ipv6-socket-address),
    }
  }
  interface instance-network {
    use network.{network};

    instance-network: func() -> network;
  }
  interface udp {
    use wasi:io/poll@0.2.3.{pollable};
    use network.{network, error-code, ip-socket-address, ip-address-family};

    record incoming-datagram {
      data: list<u8>,
      remote-address: ip-socket-address,
    }

    record outgoing-datagram {
      data: list<u8>,
      remote-address: option<ip-socket-address>,
    }

    resource udp-socket {
      start-bind: func(network: borrow<network>, local-address: ip-socket-address) -> result<_, error-code>;
      finish-bind: func() -> result<_, error-code>;
      %stream: func(remote-address: option<ip-socket-address>) -> result<tuple<incoming-datagram-stream, outgoing-datagram-stream>, error-code>;
      local-address: func() -> result<ip-socket-address, error-code>;
      remote-address: func() -> result<ip-socket-address, error-code>;
      address-family: func() -> ip-address-family;
      unicast-hop-limit: func() -> result<u8, error-code>;
      set-unicast-hop-limit: func(value: u8) -> result<_, error-code>;
      receive-buffer-size: func() -> result<u64, error-code>;
      set-receive-buffer-size: func(value: u64) -> result<_, error-code>;
      send-buffer-size: func() -> result<u64, error-code>;
      set-send-buffer-size: func(value: u64) -> result<_, error-code>;
      subscribe: func() -> pollable;
    }

    resource incoming-datagram-stream {
      receive: func(max-results: u64) -> result<list<incoming-datagram>, error-code>;
      subscribe: func() -> pollable;
    }

    resource outgoing-datagram-stream {
      check-send: func() -> result<u64, error-code>;
      send: func(datagrams: list<outgoing-datagram>) -> result<u64, error-code>;
      subscribe: func() -> pollable;
    }
  }
  interface udp-create-socket {
    use network.{network, error-code, ip-address-family};
    use udp.{udp-socket};

    create-udp-socket: func(address-family: ip-address-family) -> result<udp-socket, error-code>;
  }
  interface tcp {
    use wasi:io/streams@0.2.3.{input-stream, output-stream};
    use wasi:io/poll@0.2.3.{pollable};
    use wasi:clocks/monotonic-clock@0.2.3.{duration};
    use network.{network, error-code, ip-socket-address, ip-address-family};

    enum shutdown-type {
      receive,
      send,
      both,
    }

    resource tcp-socket {
      start-bind: func(network: borrow<network>, local-address: ip-socket-address) -> result<_, error-code>;
      finish-bind: func() -> result<_, error-code>;
      start-connect: func(network: borrow<network>, remote-address: ip-socket-address) -> result<_, error-code>;
      finish-connect: func() -> result<tuple<input-stream, output-stream>, error-code>;
      start-listen: func() -> result<_, error-code>;
      finish-listen: func() -> result<_, error-code>;
      accept: func() -> result<tuple<tcp-socket, input-stream, output-stream>, error-code>;
      local-address: func() -> result<ip-socket-address, error-code>;
      remote-address: func() -> result<ip-socket-address, error-code>;
      is-listening: func() -> bool;
      address-family: func() -> ip-address-family;
      set-listen-backlog-size: func(value: u64) -> result<_, error-code>;
      keep-alive-enabled: func() -> result<bool, error-code>;
      set-keep-alive-enabled: func(value: bool) -> result<_, error-code>;
      keep-alive-idle-time: func() -> result<duration, error-code>;
      set-keep-alive-idle-time: func(value: duration) -> result<_, error-code>;
      keep-alive-interval: func() -> result<duration, error-code>;
      set-keep-alive-interval: func(value: duration) -> result<_, error-code>;
      keep-alive-count: func() -> result<u32, error-code>;
      set-keep-alive-count: func(value: u32) -> result<_, error-code>;
      hop-limit: func() -> result<u8, error-code>;
      set-hop-limit: func(value: u8) -> result<_, error-code>;
      receive-buffer-size: func() -> result<u64, error-code>;
      set-receive-buffer-size: func(value: u64) -> result<_, error-code>;
      send-buffer-size: func() -> result<u64, error-code>;
      set-send-buffer-size: func(value: u64) -> result<_, error-code>;
      subscribe: func() -> pollable;
      shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code>;
    }
  }
  interface tcp-create-socket {
    use network.{network, error-code, ip-address-family};
    use tcp.{tcp-socket};

    create-tcp-socket: func(address-family: ip-address-family) -> result<tcp-socket, error-code>;
  }
  interface ip-name-lookup {
    use wasi:io/poll@0.2.3.{pollable};
    use network.{network, error-code, ip-address};

    resource resolve-address-stream {
      resolve-next-address: func() -> result<option<ip-address>, error-code>;
      subscribe: func() -> pollable;
    }

    resolve-addresses: func(network: borrow<network>, name: string) -> result<resolve-address-stream, error-code>;
  }
}