SadServers API

SadServers API


SadServers provides an API, the main purpose of which is to create scenario instances programmatically. This is a feature that is not available to regular users; if you want to use it please contact us at

There’s an API web interface provided.

The endpoints are (they require user account authentication and they all end in /):

  • /api/scenarios/
    • it’s read-only (GET)
    • returns the list of available scenarios
    • /api/scenarios/1/
      • returns the details of a scenario id (1 in this example)
  • /api/vms/
    • it’s read-only (GET)
    • returns the list of VMs (server instances) the authenticated user has used.
    • /api/vms/12345/
      • GET: returns the details of a VM with that id (12345 in the example)
      • DELETE: deletes (shuts down) the VM (not the db entry or endpoint)
  • /api/sessions/
    • GET: it returns the list of sessions for the authenticated user. A session is the request by a user to have a VM of a particular scenario.
    • POST: (only available to paying customers) it creates a session and an on-demand VM with a public IP for a given scenario id.
    • /api/sessions/54321/
      • GET only: returns the details of a session given its id (54321 in the example)

Command Line Test

Before getting to code, you probably want to do a basic test from the command line. You can use for example curl or httpie which is friendlier in building the requests and in formatting the replies output (for example to install in Mac: pip install httpie).

The API at the moment only accepts HTTP basic authentication. Token-based authentication is preferred and TODO. Note that only HTTPS access is allowed and there is currently no confidential information accessible with the credentials since none are requested or stored.

Test authentication with any endpoint, for example (using httpie):
http --auth $user:$pass --body

Note that if your password has special characters that can be interpreted by your shell (like bang !), then you need so wrap the password around single quotes '.

You can take out the --body option in the http command to see the reply headers as well.

Using curl adds an extra step of encoding your $user:$pass string in Base64.

If you have the base64 utility (Mac), you can do; base64userpass=$(echo -n "$user:$pass" |base64) , otherwise you can use openssl: base64userpass=$(echo -n "$user:$pass" | openssl base64)

The equivalent command in curl to request the scenarios would be:

curl  -H "Authorization: Basic $base64userpass"

Endpoint and Object Specs

All requests will return an HTTP code and a json body if the request is processed by the API. Some web server errors and messages can return as HTML (for example, doing a request to http:// instead of https:// will result in a 301 HTML message).

The API can return these codes:

  • 200 OK
  • 201 Created
  • 204 No Content
  • 400 Bad Request
  • 403 Forbidden
  • 404 Not Found
  • 500 Internal Server Error


The scenario object result on GET looks like:

    "id": 1,
    "name": "\"Manhattan\": can't write data into database.",
    "slug": "manhattan",
    "level": 2,
    "creation": "2022-06-13T00:50:39.784329Z",
    "time": 20,
    "agent": true,
    "description": "Your objective is to be able to...",
    "test": "<kbd>sudo -u postgres ..."


  • id: the id you want to POST to /api/sessions/ to create a new VM
  • name: human name
  • slug: short name, shows up in sessions as scenario field for easier identification
  • level: 1=Easy, 2=Medium, 3=Hard
  • creation: time of creating
  • time: default allowed time for the scenario, after which the VM will be terminated. This can be overridden as an optional parameter when creating a new VM.
  • agent: if the VM has a script to check for the solution
  • description: the problem statement in HTML format
  • test: the test for valid solution in HTML format (run the script for the exact test)


The VM object result on GET looks like:

    "id": 123456,
    "creation": "2024-01-14T18:03:42.101403Z",
    "instance": "i-05380b67357dfa706",
    "status": "R",
    "ip_pub": "",
    "lifespan": 77,
    "start": "2024-01-14T18:03:57.792011Z",
    "responsive": "2024-01-14T18:03:58.526456Z",
    "end": null,
    "os": "Debian 11",
    "solmd5": "aa374ea1d851fd6cac6f4d54a6061db3",
    "replay": "False"


  • id
  • creation: record creation time
  • instance: AWS instance identifier
  • status: the state of the instance. A simplification of the AWS instance states:
    • CREATING = ‘C’ (pending)
    • RUNNING = ‘R’
    • STOPPED = ‘S’
    • TERMINATED = ‘T’
  • ip_pub: the public IP of the instance
  • start: instance start time (not responsive yet, takes about a minute)
  • lifespan: the time in minutes after start time when the instance will be terminated, with an extra ome minute “readiness” period. Default is scenario.time but can be sent as a parameter when creating the VM.
  • responsive: when the VM is detected as responsive (may not be exact).
  • end: when the instance was terminated by the garbage collector.
  • os: the Linux version. This affects the SSH user when logging in; the default user is admin for Debian distros and ubuntu for Ubuntu systems.
  • solmd5: the md5 string that needs to be passed to the VM solution checker agent (code that runs ~/agent/ script) in order to check the solution remotely
  • replay: if shell command session video replay is enabled in this VM. (See Oher)

DELETE will shut down the VM instance. If succesful the call returns HTTP 204 No Content. The call is blocking with a timeout of 5 seconds and typically takes about 2 seconds to return.

If the VM doesn’t exist or if you are not the creator of the VM, the request with return 404.

The VM object (database entry and endpoint) remains and its status field is changed to ‘T’ (terminated). This call can be made without checking the initial status of the VM (ie, if it’s in Running or ‘R’ state), as in it will run without error on an already terminated VM instance. Still, you may want to check the VM status before and after the call.

DELETEs are per VM id at the moment (no hability to bulk delete VMs).

Example of deleting VM with id 123456 using httpie: http --auth $user:$pass DELETE


The Session object on GET looks like:

    "id": 54321,
    "creation": "2024-01-14T01:17:41.632635Z",
    "scenario_id": 27,
    "scenario": "paris",
    "vm_id": 12345,
    "vm_instance": "i-0b20206749febe9d3",
    "solved": 0,
    "replay_url": ""


  • id
  • creation: when the record was created
  • scenario_id: the
  • scenario: scenario.slug
  • vm_id:
  • vm_instance: vm.instance
  • solved: time in seconds the user took to solve the scenario or 0 if not solved (this is populated only from the website when user clicks “Check My Solution”)
  • replay_url: the URL of the command replay video, available for VMs with replay: True created from the SadServers website (not available to the API at the moment).

POST: Request a POST against the /api/sessions/ endpoint to create a new VM with the parameters:

  • scenario_id: integer, required, a scenario id from /api/scenarios/
  • vm_lifespan: integer, optional, the life span in minutes of the VM (if not provided, the scenario time field is used). At the moment there’s a limit of 480 minutes (8 hours) for this parameter.

Creating a VM for scenario id = 1 looks like: http --auth $user:$pass --body POST scenario_id=1

Creating a VM for scenario id = 1 that lasts for an hour looks like: http --auth $user:$pass --body POST scenario_id=1 vm_lifespan=60

After POSTing you should get back a “201 Created” reply that looks like:

    "creation": "2024-01-14T18:46:07.759410Z",
    "id": 54321,
    "scenario": "manhattan",
    "scenario_id": 1,
    "solved": 0,
    "vm_id": null,
    "vm_instance": null

Successive GETs to the /api/sessions/123456/ endpoint (where 54321 is the example of the new session id you created) should return the vm_id and from there you can get the VM details like its ip address.

Workflow for Creating a VM

Let’s put everything together in a “happy path” workflow for creating a new VM:

  1. Determine the scenario (id) you want for your VM (and optionally, its life span, say 60 minutes). For example scenario id = 1. We can check with /api/scenarios/1/

  2. Make a POST against /api/sessions/ passing as data scenario_id=1 and optionally vm_lifespan=60. Check that it returned a “201 Created” HTTP code. Note that in the reply from this POST the vm_id is not yet available.

  3. Make note of the session id returned in from your POST in the previous step (say 54321). Wait a few seconds (5 - 10 should be plenty) or do a loop to retry and do a GET to /api/sessions/ with that session id: /api/sessions/54321/. That should return a vm_id value, say 12345.

  4. The VM is still being created for the next minute or so. You can query the VM endpoint with the vm id you got from your session: /api/vms/12345/ and inspect the VM responsive field to see when the VM is actually available (it may be still a bit more time after). Note that the ip_pub field may be populated before the instance is fully ready (you can still grab its value).

  5. After using the VM (see SSH and checking the solution below), please DELETE it if you no longer need it.

SSH into your VM

You can connect to a running and ready instance with:

ssh -i ~/.ssh/private_key  -o "StrictHostKeyChecking no" $user@$ip_pub


  • private_key : corresponds to the public SSH key you indicated in your dashboard
  • -o "StrictHostKeyChecking no" : optional for automation (no prompt to confirm).
  • $user is the default user for the VM operating system. The default is admin for Debian, the most common scenario VM distribution. The Linux distribution is shown in the VM object os field. If the VM is of Ubuntu type, then use ubuntu as the user to log in as.
  • $ip_pub is the IP to connect to; the ip_pub field for your VM.

Checking the Solution

(Not available for scenarios with “agent”: false).

Locally (in the VM SSH shell) by running ~/agent/ It returns “OK” (valid solution) or “NO”.

Remotely, by accessing the endpoint http://$ip_pub:6767/checkscript/$solmd5 , where $ip_pub is the IP of the VM and $solmd5 is an MD5 hash from the field solmd5 in a VM object. This returns:

  • HTTP Code 200 and string “True” if solution is correct (if running returns OK)
  • HTTP Code 200 and string “False” if solution is incorrect ( returns NO)
  • HTTP Code 500 and string “False” if there’s a problem verifying the script (for example, the file was modified)

A basic check to test that the solution checking agent in the VM is running and accesible is to go to http://$ip_pub:6767/ping , which should result in the reply “pong”.


  • Replays : Only some VM images are set up to store the command session history replay video (for example: Kihei and Paris). VMs created via API store their replay videos privately but they don’t upload the replay sessions into the public
  • Recent scenarios VM images store in /etc/sadscenario the slug (name) of the scenario.