Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API Documentation #90

Open
johncoles opened this issue Oct 8, 2024 · 8 comments
Open

API Documentation #90

johncoles opened this issue Oct 8, 2024 · 8 comments

Comments

@johncoles
Copy link

I see the code has now been shared so I may go through and do this myself, but it would be nice to have documentation for the HTTP API used.

I note so far that there is a request to /api/auth/login to perform the login, but the password is not sent as plaintext. Subsequent requests to the API seem to use a token that is set as a cookie.

Other endpoints include:
GET /api/vm/gpio/led which returns the LED state:
{"code":0,"msg":"success","data":{"pwr":false,"hdd":false}}

GET /api/storage/images/mounted
{"code":0,"msg":"success","data":{"file":""}}

POST /api/vm/gpio with data like: {"type":"power","duration":800} will press the power button for 800ms.
Returns:
{"code":0,"msg":"success","data":null}

It would be great to have docs on how to use this to integrate in to other systems (I would like to add an integration in to Home Assistant for example) to control multiple machines.

@wj-xiao
Copy link
Collaborator

wj-xiao commented Oct 10, 2024

Please refer to these files for now. A more detailed document will be provided later.

@patschi
Copy link

patschi commented Oct 10, 2024

I took some time reverse-engineering API calls and crafting a bash-curl-way of interacting with the API. Pretty surely also easy to adapt in other programming languages.

Note: Some API paths seems to mismatch between "reality" (NanoKVM with latest 2.0.9 as of today) and today's available code on GitHub: For example, /storage/image/mounted fails, while /storage/images/mounted works. I believe the code is newer than most recent publicly available firmware.

It does not include all available API calls (such as application which doesn't work as of today or scripts) as I could/want not test them. But they do offer API endpoints. With below, it should be possible to build something together yourself.

I thought it might be useful for others, so here we go:

AUTH

Note:

  • I decided to use jq for urlencode and prettify JSON output. It was easier than using just curl or purely bash.
  • The entered password sent to API is previously encrypted using AES-256 using a hardcoded secret key (probably because website should be used with HTTP for performance reasons). This key defaults to nanokvm-sipeed-2024 and must be identical on frontend and backend (see encrypt.go for backend, and encrypt.ts for frontend)
  • If this looks painful complicated, then you can imagine how it felt figuring out below.
# Login to NanoKVM and receive token
# Adjust variables below
HOSTNAME="192.168.0.1"
USERNAME="admin"
PASSWORD="admin"

# Usually hardcoded and no need to change
PWSECKEY="nanokvm-sipeed-2024"

# Encrypt password via AES-256-CBC before sending to API
PASSENC=$(echo -n "$PASSWORD" | openssl enc -aes-256-cbc -base64 -salt -md md5 -pass pass:$PWSECKEY 2>/dev/null)
# Build JSON string for login, urlencode values as backend expects
AUTHJSON=$(jq -crnM --arg u "$USERNAME" --arg p "$PASSENC" '{"username":$u|@uri,"password":$p|@uri}')

# Send request via curl to NanoKVM
REQUEST=$(curl -s -X POST http://$HOSTNAME/api/auth/login -H 'Content-Type: application/json' --data-raw "$AUTHJSON")

# Parse token via jq from above request
TOKEN=$(echo $REQUEST | jq -r .data.token)
# Set cookie variable to be used by curl
COOKIE="nano-kvm-token=$TOKEN"

After this, below commands can be used.

GENERAL (named "VM" in code)

Get device info

# curl -s -b $COOKIE http://$HOSTNAME/api/vm/info
{"code":0,"msg":"success","data":{"ip":"<IP>","mdns":"kvm-<LAST 4 DIGITS OF MAC>.local","image":"2024-08-17-18-13-713161.img","firmware":"2.0.9","deviceKey":"<ID>"}}

Get GPIO LED state

# curl -s -b $COOKIE http://$HOSTNAME/api/vm/gpio/led
{"code":0,"msg":"success","data":{"pwr":true,"hdd":false}}

Trigger power switch

Note:

  • 800 is probably 0.8s and is considered a short click, e.g. turning on or sending shutdown request to host.
  • 8000 is considered a long click, e.g. holding power button and letting the host crash.
# curl -s -X POST -b $COOKIE http://$HOSTNAME/api/vm/gpio -H "Content-Type: application/json" -d '{"type":"power","duration":800}'
{"code":0,"msg":"success","data":null}

STREAM

Get current image and save to image.jpg

Note: This uses ffmpeg to convert the mjpeg stream to a image with name image.jpg.

# curl -s -N -b $COOKIE http://$HOSTNAME/api/stream/mjpeg -o - | ffmpeg -y -i - -frames:v 1 -vcodec mjpeg image.jpg

If you want to do something fancy:

# tesseract image.jpg -
Warning: Invalid resolution 0 dpi. Using 70 instead.
Estimating resolution as 239
{C 8066 .3211711 vyos—configi39181: Configuration success
Welcome to VyOS — vyos ttyl

vyos login: vyos
[...]

Record the stream to x264-encoded file

Note:

  • This records for 30 seconds and then exits. You can adjust the time with -t.
  • The error unable to decode APP fields: Invalid data found when processing input is repeatedly thrown. I could not figure out why, but the output video still works.
  • Regarding file sizes: Terminal screen with just a blinking cursor takes approx. 800 KB for 3 seconds with x264 encoding (-c:v libx264). In comparison, x265 (use -c:v libx265) requires approx. 200 KB instead, but comes with higher CPU cost.
# curl -s -N -b $COOKIE http://$HOSTNAME/api/stream/mjpeg -o - | ffmpeg -y -use_wallclock_as_timestamps 1 -f mjpeg -i - -an -t 30 -c:v libx264 -f mp4 output.mp4

Type text automatically

# curl -s -X POST -b $COOKIE http://$HOSTNAME/api/hid/paste -H "Content-Type: application/json" -d '{"content": "this is a test"}'
{"code":0,"msg":"success","data":null}

# curl -s -X POST -b $COOKIE http://$HOSTNAME/api/hid/paste -H "Content-Type: application/json" -d '{"content": "vyos\nvyos\n"}'
{"code":0,"msg":"success","data":null}

Examples:
image
image

Reset HID

# curl -s -X POST -b $COOKIE http://$HOSTNAME/api/hid/reset
{"code":0,"msg":"success","data":null}

Get current frame detection state

Note: Not specifying -X makes it use GET by default.

# curl -s -b $COOKIE http://$HOSTNAME/api/stream/mjpeg/detect
{"code":0,"msg":"success","data":{"enabled":false}}

Toggle frame detection

# curl -s -X POST -b $COOKIE http://$HOSTNAME/api/stream/mjpeg/detect
{"code":0,"msg":"success","data":{"enabled":true}}
# curl -s -X POST -b $COOKIE http://$HOSTNAME/api/stream/mjpeg/detect
{"code":0,"msg":"success","data":{"enabled":false}}

STORAGE

Get available images

# curl -s -b $COOKIE http://$HOSTNAME/api/storage/images | jq -r
{
  "code": 0,
  "msg": "success",
  "data": {
    "files": [
      "/data/ubuntu-24.04-desktop-amd64.iso"
    ]
  }
}

Change mounted image

Note: Path is absolute path to ISO file

# curl -s -X POST -b $COOKIE http://$HOSTNAME/api/storage/image/mount -H "Content-Type: application/json" -d '{"file": "/data/ubuntu-24.04-desktop-amd64.iso"}'
{"code":0,"msg":"success","data":null}

Get currently mounted image

# curl -s -b $COOKIE http://$HOSTNAME/api/storage/images/mounted
{"code":0,"msg":"success","data":{"file":"/data/ubuntu-24.04-desktop-amd64.iso"}}

VIRTUAL DEVICES

Get current virtual devices state

# curl -s -b $COOKIE http://$HOSTNAME/api/vm/device/virtual
{"code":0,"msg":"success","data":{"network":true,"disk":true}}

Set/Toggle state of virtual device

Note: Use network or disk for device key

$ curl -s -X POST -b $COOKIE http://$HOSTNAME/api/vm/device/virtual -H "Content-Type: application/json" -d '{"device": "network"}'
{"code":0,"msg":"success","data":{"on":true}}

$ curl -s -X POST -b $COOKIE http://$HOSTNAME/api/vm/device/virtual -H "Content-Type: application/json" -d '{"device": "network"}'
{"code":0,"msg":"success","data":{"on":false}}

@PatrickHuetter
Copy link

Nice! Thanks for your work and sharing this!

@FlorianHeigl
Copy link

kudos for the tesseract example, more people should figure out stuff like this and do it!

@FlorianHeigl
Copy link

@wj-xiao Hi! is there a non-authenticated local API endpoint for scripts running on the device? Would be easier for sharing, unless someone says it's bad for security and I just didn't notice a problem there.

@wj-xiao
Copy link
Collaborator

wj-xiao commented Nov 8, 2024

@FlorianHeigl By default, APIs require authentication.

However, you can disable authentication by modifying the configuration file.
Add one line authentication: disable to the configuration file /etc/kvm/server.yaml. And restart the service by /etc/init.d/S95nanokvm restart.

Please note that this will make the device insecure.

@adamponi
Copy link

Is there an API that will allow me to click? Also press keys

@patschi
Copy link

patschi commented Nov 22, 2024

Is there an API that will allow me to click? Also press keys

Could not find a specific API call last time I checked, so I assume it's sent through the established websocket connection. Possible for sure, but way more tricky.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants