{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://remotenx.app/paket/remotenx-package.schema.json",
  "title": "remoteNX package",
  "description": "A remoteNX package (.rnxconfig, UTI at.fellner.remotenx.package, application/vnd.remotenx.package+json, at most 512 KB): connections, groups, tunnels, Horizon servers, snippets, pinned host keys and identity names that become the user's own, editable items. The sections are the MDM configuration's (https://remotenx.app/mdm/remotenx-config.schema.json); policies, the organisation, user info, trust, certificate authorities, passwords, private keys, managed identities and Shortcuts are never allowed. See docs/package/FORMAT.md. Generated by Scripts/make-package-schema.py.",
  "type": "object",
  "required": [
    "format",
    "version",
    "config"
  ],
  "additionalProperties": false,
  "properties": {
    "format": {
      "const": "remotenx-package"
    },
    "version": {
      "type": "integer",
      "const": 1,
      "description": "The format version. A newer version is refused with a note that a newer remoteNX is needed."
    },
    "name": {
      "type": "string",
      "maxLength": 80,
      "description": "Shown in the import preview."
    },
    "description": {
      "type": "string",
      "maxLength": 2000
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    },
    "createdWith": {
      "type": "string",
      "maxLength": 200
    },
    "config": {
      "type": "object",
      "description": "The MDM schema's sections that a package may carry.",
      "additionalProperties": false,
      "properties": {
        "schemaVersion": {
          "type": "integer",
          "const": 1,
          "description": "The MDM schema version the sections follow. Optional in a package; 1."
        },
        "groups": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/group"
          },
          "maxItems": 500
        },
        "connections": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/connection"
          },
          "maxItems": 500
        },
        "tunnels": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/tunnel"
          },
          "maxItems": 500
        },
        "horizonServers": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/horizonServer"
          },
          "maxItems": 500
        },
        "snippets": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/snippet"
          },
          "maxItems": 500
        },
        "knownHosts": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/knownHost"
          },
          "maxItems": 500
        },
        "identities": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/identity"
          },
          "maxItems": 500
        }
      }
    }
  },
  "$defs": {
    "group": {
      "type": "object",
      "properties": {
        "id": {
          "type": [
            "string",
            "integer"
          ],
          "description": "Links entries inside the package (connection to tunnel, identity, group, Horizon server). The import gives every item a new identity of its own; integers are read as strings."
        },
        "name": {
          "type": "string"
        },
        "color": {
          "type": "string",
          "description": "Colour tag of the app's palette (for example `blue`, `green`, `orange`, `red`, `purple`)."
        },
        "symbol": {
          "type": "string",
          "description": "SF Symbol name, for example server.rack or building.2."
        },
        "sortOrder": {
          "type": "integer"
        }
      },
      "required": [
        "id",
        "name"
      ],
      "additionalProperties": true,
      "propertyNames": {
        "not": {
          "enum": [
            "password",
            "privateKey",
            "passphrase",
            "managedIdentity",
            "preConnectShortcut",
            "postDisconnectShortcut"
          ]
        }
      }
    },
    "connection": {
      "type": "object",
      "properties": {
        "id": {
          "type": [
            "string",
            "integer"
          ],
          "description": "Links entries inside the package (connection to tunnel, identity, group, Horizon server). The import gives every item a new identity of its own; integers are read as strings."
        },
        "name": {
          "type": "string"
        },
        "protocol": {
          "type": "string",
          "enum": [
            "rdp",
            "ssh",
            "blast",
            "vnc",
            "citrix"
          ]
        },
        "host": {
          "type": "string",
          "minLength": 1
        },
        "port": {
          "type": "integer",
          "minimum": 1,
          "maximum": 65535
        },
        "username": {
          "type": "string",
          "description": "Templates such as {{username}} stay as written; the user fills them in after the import."
        },
        "domain": {
          "type": "string"
        },
        "group": {
          "type": "string",
          "minLength": 1,
          "description": "The `id` of an entry in `groups`."
        },
        "tags": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "color": {
          "type": "string",
          "description": "Colour tag of the app's palette (for example `blue`, `green`, `orange`, `red`, `purple`)."
        },
        "tunnel": {
          "type": "string",
          "minLength": 1,
          "description": "The `id` of an entry in `tunnels`."
        },
        "identity": {
          "type": "string",
          "minLength": 1,
          "description": "The `id` of an entry in `identities`."
        },
        "preConnectTimeoutSeconds": {
          "type": "integer",
          "minimum": 5,
          "maximum": 120,
          "default": 20,
          "description": "Seconds to wait for the host after the shortcut; values outside 5 to 120 are clamped."
        },
        "rdp": {
          "type": "object",
          "properties": {
            "gateway": {
              "type": "string",
              "description": "RD Gateway host[:port]."
            },
            "nla": {
              "type": "boolean"
            },
            "clipboard": {
              "type": "boolean"
            },
            "driveRedirection": {
              "type": "boolean"
            },
            "driveRedirectionReadOnly": {
              "type": "boolean"
            },
            "microphone": {
              "type": "boolean"
            },
            "audio": {
              "type": "boolean"
            },
            "colorDepth": {
              "type": "integer",
              "enum": [
                16,
                32
              ]
            },
            "scaleFactor": {
              "type": "number",
              "minimum": 1,
              "maximum": 3,
              "description": "1.0 native points, 2.0 Retina pixels; absent: automatic."
            },
            "kerberosRealm": {
              "type": "string"
            },
            "kerberosKDC": {
              "type": "string"
            }
          },
          "additionalProperties": true,
          "propertyNames": {
            "not": {
              "enum": [
                "password",
                "privateKey",
                "passphrase",
                "managedIdentity",
                "preConnectShortcut",
                "postDisconnectShortcut"
              ]
            }
          }
        },
        "ssh": {
          "type": "object",
          "properties": {
            "identity": {
              "type": "string",
              "minLength": 1,
              "description": "The `id` of an entry in `identities`."
            },
            "tunnel": {
              "type": "string",
              "minLength": 1,
              "description": "The `id` of an entry in `tunnels`."
            },
            "startCommand": {
              "type": "string"
            },
            "terminalType": {
              "type": "string"
            },
            "keepAliveSeconds": {
              "type": "integer",
              "minimum": 0
            }
          },
          "additionalProperties": true,
          "propertyNames": {
            "not": {
              "enum": [
                "password",
                "privateKey",
                "passphrase",
                "managedIdentity",
                "preConnectShortcut",
                "postDisconnectShortcut"
              ]
            }
          }
        },
        "blast": {
          "type": "object",
          "properties": {
            "server": {
              "type": "string",
              "minLength": 1,
              "description": "The `id` of an entry in `horizonServers`."
            },
            "desktopName": {
              "type": "string"
            },
            "launch": {
              "type": "string",
              "enum": [
                "htmlAccess",
                "officialClient"
              ]
            },
            "application": {
              "type": "boolean"
            }
          },
          "additionalProperties": true,
          "propertyNames": {
            "not": {
              "enum": [
                "password",
                "privateKey",
                "passphrase",
                "managedIdentity",
                "preConnectShortcut",
                "postDisconnectShortcut"
              ]
            }
          }
        },
        "vnc": {
          "type": "object",
          "description": "A VNC screen: macOS Screen Sharing or a standard VNC server (default port 5900). The connection's `username` is the macOS account; the password is asked at every connection.",
          "properties": {
            "authentication": {
              "type": "string",
              "enum": [
                "password",
                "macAccount",
                "none"
              ],
              "description": "`password`: a VNC password; `macAccount`: a macOS user name and password; `none`: no authentication. Absent: `macAccount` with a `username`, otherwise `password`."
            },
            "colorDepth": {
              "type": "integer",
              "enum": [
                24,
                16,
                8
              ]
            },
            "quality": {
              "type": "string",
              "enum": [
                "automatic",
                "lossless"
              ],
              "description": "`automatic` offers Tight with JPEG first, `lossless` only lossless encodings."
            },
            "scaling": {
              "type": "string",
              "enum": [
                "fit",
                "fitWidth",
                "actualSize"
              ],
              "description": "`fit` shows the whole screen, `fitWidth` fills the view's width and scrolls vertically, `actualSize` one remote pixel per device pixel."
            },
            "viewOnly": {
              "type": "boolean",
              "description": "No pointer or keyboard input reaches the screen."
            },
            "clipboard": {
              "type": "boolean",
              "description": "Offers Type Clipboard Text in the session; `policies.allowClipboard: false` turns it off."
            },
            "shared": {
              "type": "boolean",
              "description": "Keeps other viewers connected (RFB shared flag). Default true."
            },
            "unlockAutomatically": {
              "type": "boolean",
              "description": "Ignored since 1.2.4 (18): VNC cannot tell whether the Mac is locked, and on an unlocked Mac the password went into the app in front. Unlock Mac stays a manual action."
            },
            "unlockWhenLocked": {
              "type": "boolean",
              "description": "Unlock Automatically (since 1.2.4 (19)), with `macAccount` and `encryptWithSSH`: after the first screen the app checks over SSH that the Mac shows the lock screen of the connection's account, checks again half a second later, and only then types the password entered at sign-in, followed by Return. Never types when the Mac is unlocked, at its login window, locked for another account, or the check fails. Default false."
            },
            "encryptWithSSH": {
              "type": "boolean",
              "description": "Encrypt with SSH: the session runs through the host's SSH server (a Mac's Remote Login must be on), forwarded to the screen's port on the host's loopback, and signs in to SSH with the connection's account and the password entered at connect. With a `tunnel`, the host is the last hop after the tunnel's server. Default false."
            },
            "sshPort": {
              "type": "integer",
              "minimum": 1,
              "maximum": 65535,
              "description": "The host's SSH port for `encryptWithSSH`. Default 22."
            },
            "lockOnDisconnect": {
              "type": "boolean",
              "description": "Lock Mac When Disconnecting: before a disconnect the user asks for, Control-Command-Q (Lock Screen) is typed on the Mac. Not on a lost connection or with viewOnly. Default false."
            }
          },
          "additionalProperties": true,
          "propertyNames": {
            "not": {
              "enum": [
                "password",
                "privateKey",
                "passphrase",
                "managedIdentity",
                "preConnectShortcut",
                "postDisconnectShortcut"
              ]
            }
          }
        },
        "citrix": {
          "type": "object",
          "description": "A Citrix store (StoreFront or Citrix Workspace), opened in the app's web client; the user signs in on the store's own page and remoteNX keeps no password. The connection's `username` is shown as a reminder only. Citrix stores never go through a `tunnel`.",
          "properties": {
            "storeURL": {
              "type": "string",
              "pattern": "^https://",
              "description": "The store web site, for example `https://citrix.example.com/Citrix/StoreWeb/`, or a Citrix Workspace URL `https://customer.cloud.com`. HTTPS only. Absent: the connection's `host` (and `port`) with the default path `/Citrix/StoreWeb/`."
            },
            "openLaunchesIn": {
              "type": "string",
              "enum": [
                "inApp",
                "workspaceApp",
                "ask"
              ],
              "description": "Where an app or desktop opens when the store hands out a launch file (.ica): `inApp` keeps the store's web client (Citrix Workspace for HTML5) and offers the hand-off only for launch files, `workspaceApp` hands every launch file to the Citrix Workspace app at once, `ask` (default) asks each time."
            },
            "allowHTML5": {
              "type": "boolean",
              "description": "Offer Try in Browser: apps and desktops may run in Citrix Workspace for HTML5 inside the app, where the store allows it. Default true."
            },
            "trustedHosts": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Hosts the store's pages may show in the web view besides the store and the gateway, typically the identity provider (`login.example.com`), or `*.example.com` for a domain. Any other host opens only in Safari, after a question. A Citrix Workspace URL also allows Citrix Cloud's own sign-in (`*.cloud.com`)."
            },
            "gateway": {
              "type": "string",
              "description": "NetScaler Gateway host in front of the store; its pages are allowed in the web view."
            }
          },
          "additionalProperties": true,
          "propertyNames": {
            "not": {
              "enum": [
                "password",
                "privateKey",
                "passphrase",
                "managedIdentity",
                "preConnectShortcut",
                "postDisconnectShortcut"
              ]
            }
          }
        }
      },
      "required": [
        "id",
        "protocol"
      ],
      "anyOf": [
        {
          "required": [
            "host"
          ]
        },
        {
          "description": "A Horizon desktop or app without a host of its own: host, port, domain, launch and tunnel come from blast.server.",
          "required": [
            "protocol",
            "blast"
          ],
          "properties": {
            "protocol": {
              "const": "blast"
            },
            "blast": {
              "required": [
                "server"
              ]
            }
          }
        },
        {
          "description": "A Citrix store named by citrix.storeURL instead of a host.",
          "required": [
            "protocol",
            "citrix"
          ],
          "properties": {
            "protocol": {
              "const": "citrix"
            },
            "citrix": {
              "required": [
                "storeURL"
              ]
            }
          }
        }
      ],
      "additionalProperties": true,
      "propertyNames": {
        "not": {
          "enum": [
            "password",
            "privateKey",
            "passphrase",
            "managedIdentity",
            "preConnectShortcut",
            "postDisconnectShortcut"
          ]
        }
      },
      "description": "In a package: no password, no Shortcuts to run; username templates such as {{username}} stay for the user to fill in."
    },
    "tunnel": {
      "type": "object",
      "description": "An SSH tunnel (ssh -L) connections reach their host through.",
      "properties": {
        "id": {
          "type": [
            "string",
            "integer"
          ],
          "description": "Links entries inside the package (connection to tunnel, identity, group, Horizon server). The import gives every item a new identity of its own; integers are read as strings."
        },
        "name": {
          "type": "string"
        },
        "host": {
          "type": "string",
          "minLength": 1
        },
        "port": {
          "type": "integer",
          "minimum": 1,
          "maximum": 65535
        },
        "identity": {
          "type": "string",
          "minLength": 1,
          "description": "The `id` of an entry in `identities`."
        },
        "username": {
          "type": "string",
          "description": "Used when there is no identity; the app asks for the password."
        },
        "localPort": {
          "type": "integer",
          "minimum": 0,
          "maximum": 65535,
          "description": "0 or absent: any free port."
        },
        "remoteHost": {
          "type": "string"
        },
        "remotePort": {
          "type": "integer",
          "minimum": 1,
          "maximum": 65535
        },
        "keepAliveSeconds": {
          "type": "integer",
          "minimum": 0
        },
        "viaTunnel": {
          "type": "string",
          "description": "The `id` of a jump tunnel (ssh -J)."
        },
        "forwardKind": {
          "type": "string",
          "enum": [
            "local",
            "remote",
            "dynamic"
          ],
          "default": "local",
          "description": "Package only. How the tunnel forwards when started on its own: ssh -L (local), -R (remote) or -D (dynamic, SOCKS)."
        },
        "bindAddress": {
          "type": "string",
          "minLength": 1,
          "description": "Package only. Where the forward listens: an IP address of the device for local and dynamic (default 127.0.0.1), on the server for remote (default localhost)."
        },
        "openURL": {
          "type": "string",
          "maxLength": 2048,
          "pattern": "^(https://[^\\s/?#@]+([/?#][^\\s]*)?|http://(127\\.0\\.0\\.1|localhost):[0-9]{1,5}([/?#][^\\s]*)?)$",
          "description": "Package only. Offered as \"Open\" while the tunnel runs, never opened without a tap. An https:// URL, or http://127.0.0.1:<localPort> / http://localhost:<localPort> of a local forward with a fixed localPort; no user name or password in the URL."
        }
      },
      "required": [
        "id",
        "host"
      ],
      "additionalProperties": true,
      "propertyNames": {
        "not": {
          "enum": [
            "password",
            "privateKey",
            "passphrase",
            "managedIdentity",
            "preConnectShortcut",
            "postDisconnectShortcut"
          ]
        }
      },
      "allOf": [
        {
          "if": {
            "required": [
              "openURL"
            ],
            "properties": {
              "openURL": {
                "pattern": "^http://"
              }
            }
          },
          "then": {
            "required": [
              "localPort"
            ],
            "properties": {
              "forwardKind": {
                "const": "local"
              },
              "localPort": {
                "minimum": 1
              }
            }
          }
        }
      ]
    },
    "horizonServer": {
      "type": "object",
      "properties": {
        "id": {
          "type": [
            "string",
            "integer"
          ],
          "description": "Links entries inside the package (connection to tunnel, identity, group, Horizon server). The import gives every item a new identity of its own; integers are read as strings."
        },
        "name": {
          "type": "string"
        },
        "kind": {
          "type": "string",
          "enum": [
            "connectionServer",
            "uag",
            "access"
          ],
          "default": "connectionServer"
        },
        "host": {
          "type": "string",
          "minLength": 1
        },
        "port": {
          "type": "integer",
          "minimum": 1,
          "maximum": 65535
        },
        "domain": {
          "type": "string"
        },
        "launch": {
          "type": "string",
          "enum": [
            "htmlAccess",
            "officialClient"
          ]
        },
        "tunnel": {
          "type": "string",
          "minLength": 1,
          "description": "The `id` of an entry in `tunnels`."
        },
        "identity": {
          "type": "string",
          "minLength": 1,
          "description": "The `id` of an entry in `identities`."
        },
        "portalPath": {
          "type": "string",
          "description": "Portal page when it is not the default for the kind, for example /catalog-portal/ui."
        }
      },
      "required": [
        "id",
        "host"
      ],
      "additionalProperties": true,
      "propertyNames": {
        "not": {
          "enum": [
            "password",
            "privateKey",
            "passphrase",
            "managedIdentity",
            "preConnectShortcut",
            "postDisconnectShortcut"
          ]
        }
      }
    },
    "snippet": {
      "type": "object",
      "properties": {
        "id": {
          "type": [
            "string",
            "integer"
          ],
          "description": "Links entries inside the package (connection to tunnel, identity, group, Horizon server). The import gives every item a new identity of its own; integers are read as strings."
        },
        "name": {
          "type": "string"
        },
        "script": {
          "type": "string"
        },
        "sendReturn": {
          "type": "boolean"
        },
        "folder": {
          "type": "string"
        }
      },
      "required": [
        "id",
        "script"
      ],
      "additionalProperties": true,
      "propertyNames": {
        "not": {
          "enum": [
            "password",
            "privateKey",
            "passphrase",
            "managedIdentity",
            "preConnectShortcut",
            "postDisconnectShortcut"
          ]
        }
      }
    },
    "knownHost": {
      "type": "object",
      "description": "A pinned SSH host key: the host connects without the first-contact prompt, and any other key is refused without a prompt.",
      "properties": {
        "host": {
          "type": "string",
          "minLength": 1
        },
        "port": {
          "type": "integer",
          "minimum": 1,
          "maximum": 65535
        },
        "keyType": {
          "type": "string",
          "description": "ssh-ed25519, ecdsa-sha2-nistp256, ssh-rsa, ..."
        },
        "key": {
          "type": "string",
          "contentEncoding": "base64",
          "description": "The SSH wire-format public key, base64: the second field of a known_hosts line."
        }
      },
      "required": [
        "host",
        "keyType",
        "key"
      ],
      "additionalProperties": true,
      "propertyNames": {
        "not": {
          "enum": [
            "password",
            "privateKey",
            "passphrase",
            "managedIdentity",
            "preConnectShortcut",
            "postDisconnectShortcut"
          ]
        }
      }
    },
    "identity": {
      "type": "object",
      "description": "Who signs in, never with what: a name and a username. The user picks or creates a key or password after the import.",
      "properties": {
        "id": {
          "type": [
            "string",
            "integer"
          ],
          "description": "Links entries inside the package (connection to tunnel, identity, group, Horizon server). The import gives every item a new identity of its own; integers are read as strings."
        },
        "name": {
          "type": "string"
        },
        "username": {
          "type": "string",
          "description": "Templates such as {{username}} stay as written; the user fills them in after the import."
        }
      },
      "required": [
        "id",
        "username"
      ],
      "additionalProperties": false
    }
  }
}
