Skip to main content

Cordial Scripting Language

Cordial Scripting Language (CSL) has a syntax vaguely reminiscent of HCL and SQL.

Names

Compared to the URL-friendly names of resources, CSL uses a more human-readable approach

<resource-type-singular> <id> [for <parent-id>] [with <extension-id>]
  • resource-type-singular is address, credential, user, etc.
  • id is the resource ID
  • parent-id is required if the resource is nested
  • extension-id is allowed if the resource has extendable names

Examples:

  • user 19
  • symbol USDC for SOL
  • credential 3 for eddie

However, note that when filtering (see List below), name fields must still be matched against their API form. For example: list users | name = "users/risk-agent", or users | name = "*/risk-*"

The singular and plural resource types can be written with or without dashes:

  • access rule 13
  • access-rule 13
  • client key 1
  • list client keys (see List below)

Get

[get] (<name> | $<variable>)
  • specifying the string "get" is optional
  • name is the full name of a resource, or
  • variable is a variable name (see below), dereferenced with $

Examples:

  • get user 19
  • symbol USDC for SOL
  • get $u (u assumed bound user variable)

List

[(get|list)] <resource-type-plural> [for <parent-id>] [ | <filter>]
  • specifying one of the strings "get" or "list" is optional
  • resource-type-plural is addresses, credentials, users, etc.
  • parent-id is allowed if the resource is nested, in which case only resources under the given parent are listed
  • filter optionally filters results

The filtering language is an instantiation of API 160 (grammar) and will be documented in more detail.

Examples:

  • users
  • addresses for SOL
  • list roles
  • operations | request.action = "update" request.resource = "Account"

Create

create [<variant>] <resource-type-singular> [<resource-id>] [for <parent-id>] [with <extension-id>] [ { <data> } ]
  • resource-type-singular is address, credential, user, etc.
  • variant is required if the resource has variants
  • parent-id is required if the resource is nested
  • extension-id is allowed if the resource has extendable names
  • data is an optional TOML inline table (subset of the corresponding resource as per API documentation)

The interpreter hangs until

  • either the resource is successfully created (which may require another user to approve),
  • or the request fails.

Examples:

  • create human user
  • create internal address for SOL
  • create role fancy-role { credentials = ["cosmos"], users = ["human"] }
  • create human user fancy-user { roles = ["roles/fancy-role"] }

Note that the data may contain fields of variables, see below.

Update

update (<name> | $<variable>) <data>
  • name or variable are full names of resources or variable names, as in Get
  • data is a TOML inline table with the new values for fields that are to be changed

The engine currently implements "replace" semantics (as per HTTP PUT). Here, we implement an equivalent of JSON Merge Patch (RFC 7396). This has the downside that we can't delete fields, as TOML has no null values. In the future, we might implement an equivalent of JSON Patch (RFC 6902) instead.

To avoid the "lost update" problem, the engine requires setting the expected next value of version; this is handled automatically.

Examples:

  • update user eddie { roles = ["roles/admin"] }
  • update $us { roles = ["roles/admin"] } (us assumed bound user variable)
  • update credential 3 for hilda { notes.description = "The red Solo 2" }

Delete

delete (<name> | $<variable>) <data>
  • name or variable are full names of resources or variable names, as in Get and Update

Examples:

  • delete symbol USDC for SOL
  • delete access rule 123
  • delete $user (user assumed bound user variable)

Variable Assignment

<variable> = ( <name> | <create> )
  • variable is a valid identifier (not a reserved keyword). The variable name _ is special, in that it cannot be referenced later.
  • name is the name of a resource in the form <resource-type-singular> <id> [for <parent-id>] [with <extension-id>] used in Get.
  • create is a create command as above

A variable is a binding of an identifier to the name of a resource, either explicitly stated, or as the result of creating a resource (if creation is successful).

Examples:

  • user1 = create human user
  • admin1 = user admin-1

Variables may later be used (prefixed by $) as argument to Get, or anywhere its name is required.

Example:

addr1 = create internal address for SOL
get $addr1
create internal account warm { addresses = [$addr1] }

The values of the resource corresponding to the variable may also be used wherever such values are required. In this case, the then current value of the resource is used.

Example:

create human user invitee
i1 = create invite client key i1 { code = "1" }
create invite credential for invitee { public_key = $i1.public_key }

Fallible Variable Assignment

<variable>, <err> = ( <create> | <update> | <delete> )
  • variable is a valid identifier. The variable name _ is special, in that it cannot be referenced later.
  • err is a valid identifier. The variable name _ is special, in that it cannot be referenced later.
  • create is a create command as above
  • update is a update command as above
  • delete is a delete command as above

If the mutation fails, err has a value, and it may be asserted. This is mostly for testing purposes: To prove an operation is denied is to attempt it and fail. The variable is only bound if creation succeeds.

Example:

a = create internal address for SOL
b = create internal address for SOL
_, err = create transfer { from = $a, to = $b, asset = "SOL", amount = "1" }
assert err
create allow transfer-rule { asset = "SOL", from = $a, to = $b }
t = create transfer { from = $a, to = $b, asset = "SOL", amount = "1" }

Assertions

assert <error>
  • error is a valid identifier

This command fails if error is not bound to an error.

For Loops

for <variable> in <list> { <command> }
  • variable is a valid identifier
  • list is a valid list command (potentially filtered)
  • command is any command, it can make use of the variable

Examples:

  • for user in users | name = "legacy-machine*" { delete $user }
  • for address in addresses | variant = "external" { update $address { labels.verified = "false" }}

Settings

set <setting> = <value>

The interpreter has several settings that maybe modified.

Examples:

  • set sign.with = <client-key>: the client-key will be used in the following
  • set display.strip = (true|false): if true, the create/update times and and states of variables will be suppressed when resources are output