Sign inSign up

ariefsn/vps-agent

By ariefsn

•Updated 11 months ago

This is the agent service for VPS, built with Go and gqlgen. It exposes vnstat and fastfetch report.

Image
Developer tools
Monitoring & observability
0

437

ariefsn/vps-agent repository overview

⁠VPS Agent (Go + gqlgen)

This is the agent service for VPS, built with Go and gqlgen. It exposes vnStat network traffic reports via a GraphQL API for consumption by web and mobile clients.

Important: The environment where this service runs must have vnstat installed and accessible on PATH. The service shells out to vnstat --json to collect metrics.

⁠Features

  • GraphQL API exposing vnStat data: totals, per-interface traffic, and estimates.
  • Query and Subscription support for periodic updates.
  • Simple HMAC-based protection for sensitive fields via a @protected directive.
  • Fastfetch system information and Vnstat exposed via GraphQL (query and streaming subscription).

⁠Prerequisites

  • Go 1.25+ (repo uses module tooling; Docker image builds against Go 1.25-alpine).
  • vnstat installed on the target VPS or container image.
    • On Debian/Ubuntu: sudo apt-get install vnstat
    • On Alpine: apk add vnstat
  • fastfetch installed and accessible on PATH — used to gather detailed system information.
  • If using Docker, you can pull a prebuilt image from Docker Hub: ariefsn/vps-agent.

⁠Environment Variables

  • MODE — development/production (default: development).
  • SALT — required; random string used for HMAC token verification.
  • PLAYGROUND — true/false to enable GraphQL Playground (default: false).
  • HOST — bind address (default: 0.0.0.0).
  • PORT — server port (default: 8080).

Example .env:

MODE=development
SALT=SomeRandomText
PLAYGROUND=false
HOST=0.0.0.0
PORT=3100

⁠Running Locally

  1. Install vnstat on your machine and ensure it is collecting data.
  2. Create a .env file (see above). SALT is required.
  3. Start the server:
    • go run ./server.go
    • The server binds to HOST:PORT. Defaults are 0.0.0.0:8080.
  4. (Optional) Enable Playground by setting PLAYGROUND=true and open http://localhost:<PORT>/graphui.

⁠Docker

  • Pull the image: docker pull ariefsn/vps-agent
  • Run the container (set your envs, ensure vnstat available in the running environment):
docker run --rm -p 3100:3100 \
  -e MODE=production \
  -e SALT=SomeRandomText \
  -e PLAYGROUND=true \
  -e HOST=0.0.0.0 \
  -e PORT=3100 \
  ariefsn/vps-agent

Notes:

  • The provided Dockerfile does not install vnstat. Ensure vnstat is available where the process runs (either install it in the image or run the service on a VPS with vnstat installed).
  • The agent also shells out to fastfetch. Ensure fastfetch is available inside the container or host environment. You may mount the binary (e.g., /usr/bin/fastfetch) into the container or build a custom image that installs fastfetch.

⁠GraphQL Overview

  • HTTP endpoint: POST /graphql
  • Playground (optional): GET /graphui when PLAYGROUND=true
  • Subscriptions: graphql-ws over /graphql (WebSocket)
⁠Schema Highlights (Fastfetch)
  • type FastfetchResult { ... } including os, cpu, gpu, memory, disk, localIp, and more
  • Queries:
    • fastfetch: FastfetchResult! @protected
  • Subscriptions:
    • fastfetch(input: FastfetchStreamInput!): FastfetchStreamResult! @protected
⁠Schema Highlights
  • enum Period { HOUR DAY MONTH YEAR }
  • type VnstatResult includes:
    • vnstatversion, jsonversion
    • total { rx tx }
    • interfaceNames: [String!]!
    • interfaces: [InterfaceResult!]!
    • estimated: EstimatedTraffic!
  • Queries:
    • ping: String!
    • vnstat(input: VnstatInput!): VnstatResult! @protected
  • Subscriptions:
    • vnstat(input: VnstatStreamInput!): VnstatStreamResult! @protected
⁠Authentication

Protected operations use headers:

  • APP-TOKEN: HMAC-SHA256 hex digest of the timestamp using SALT.
  • APP-TIMESTAMP: Unix timestamp (seconds) representing the token’s expiry. Must be in the future.

Token generation (pseudo-code):

timestamp = Math.floor(Date.now()/1000) + 3600  // valid for 1 hour
token = hex(hmac_sha256(String(timestamp), SALT))

HTTP headers for requests:

APP-TOKEN: <token>
APP-TIMESTAMP: <timestamp>
⁠Example Query

Request body:

query ($input: VnstatInput!) {
  vnstat(input: $input) {
    vnstatversion
    jsonversion
    total { rx tx }
    interfaceNames
    interfaces {
      name
      total { rx tx }
      average { rx tx total }
      monthly { rx tx total }
      traffics {
        date { year month day }
        rx
        tx
        averageRate
      }
    }
    estimated { rx tx total }
  }
}

Variables:

{ "input": { "period": "DAY", "interfaceNames": ["eth0"] } }

curl example:

curl -X POST http://localhost:3100/graphql \
  -H "Content-Type: application/json" \
  -H "APP-TOKEN: <token>" \
  -H "APP-TIMESTAMP: <timestamp>" \
  --data '{
    "query": "query($input: VnstatInput!){ vnstat(input:$input){ total{rx tx} interfaceNames estimated{rx tx total} } }",
    "variables": { "input": { "period": "DAY" } }
  }'
⁠Example Fastfetch Query

Request body:

query {
  fastfetch {
    os { prettyName version }
    cpu { cpu vendor cores { physical logical } }
    memory { total used }
    disk { name filesystem mountpoint bytes { total used free } }
    localIp { name ipv4 }
  }
}

curl example:

curl -X POST http://localhost:3100/graphql \
  -H "Content-Type: application/json" \
  -H "APP-TOKEN: <token>" \
  -H "APP-TIMESTAMP: <timestamp>" \
  --data '{
    "query": "query{ fastfetch{ os{ prettyName version } cpu{ cpu vendor cores{ physical logical } } memory{ total used } localIp{ name ipv4 } } }"
  }'
⁠Example Subscription
  • Connect using graphql-ws and provide init payload keys APP-TOKEN and APP-TIMESTAMP. Subscription document:
subscription ($input: VnstatStreamInput!) {
  vnstat(input: $input) {
    data {
      total { rx tx }
      estimated { rx tx total }
    }
    message
  }
}

Variables:

{ "input": { "period": "HOUR", "interval": 60000 } }
⁠Example Fastfetch Subscription

Subscription document:

subscription ($input: FastfetchStreamInput!) {
  fastfetch(input: $input) {
    data {
      os { prettyName version }
      cpu { cpu temperature }
      memory { total used }
    }
    message
  }
}

Variables:

{ "input": { "interval": 60000 } }

⁠Development

  • Generate GraphQL code after schema changes:
    • go run github.com/99designs/gqlgen generate
    • Or make generate.gql
  • Useful Makefile targets:
    • make linux — build Linux amd64 binary.
    • make mac / make mac-amd — build macOS arm64/amd64 binaries.
    • make windows — build Windows amd64 binary.
    • make package — archive builds into dist/.
    • make docker — build and push multi-arch image (uses IMAGE_NAME=ariefsn/vps-agent).
    • make docker.push — push images (dry-run unless PUBLISH=true).

⁠Project Structure

backend/
├── apps/                 # vnstat integration
├── graph/                # gqlgen generated + schemas + resolvers
├── helper/               # utils (token, math, conversions)
├── logger/               # logging wrappers
├── models/               # GraphQL and domain models
├── server.go             # HTTP server, GraphQL handler, auth
├── Dockerfile            # multi-stage build
├── makefile              # builds and releases
└── .env.example          # sample env vars

⁠Notes

  • vnstat must be available wherever this process runs.
  • Playground path is /graphui, GraphQL endpoint is /graphql.
  • CORS is open by default and allows common local dev origins.

⁠License

Licensed under the Apache License 2.0⁠.
© 2025 Arief Setiyo Nugroho

Tag summary

Content type

Image

Digest

sha256:ad434b122…

Size

37.9 MB

Last updated

11 months ago

docker pull ariefsn/vps-agent