Pelican Wings

repository·main·Indexed 43 days ago

https://github.com/pelican-dev/wings

A high-performance, secure server control plane for the gaming industry. Wings manages server lifecycles via an HTTP API and provides a built-in SFTP server for file management, integrating directly with the Pelican Panel.

Tokens
4.6K
Snippets
22
Records
28
Agent score
56%

What's inside Wings

  1. Overview of Pelican Wings

    main

    Wings is the server control plane for Pelican. It is designed for high performance and security in gaming environments. It provides two primary interfaces for interacting with server instances:

    1. HTTP API: Allows direct interfacing with running server instances to manage the server lifecycle, fetch logs, and generate backups.
    2. Built-in SFTP Server: Enables file access using the same credentials used for the Pelican Panel, removing the need for Pelican-specific dependencies for file management.
  2. Build Wings from source on macOS

    main

    If you are developing or want to build Wings manually on macOS, you can use the standard Go build command or the provided Makefile targets.

    Native build

    go build -o wings wings.go

    Using Makefile

    make build-darwin
    go build -o wings wings.go
  3. Run Wings on macOS

    main

    Wings can run natively on macOS, though it is primarily designed for Linux. Because Wings manages Docker containers that run Linux, it relies on Docker Desktop to provide a Linux VM. Wings itself runs on the macOS host, and containers run inside the Docker Desktop VM.

    Prerequisites

    • Docker Desktop installed and running.
    • mkcert installed (brew install mkcert) for SSL certificates.
    • A Pelican Panel instance with a node configured for this machine.

    Setup Steps

    1. Create directories

    Create the necessary configuration and data directories in your user home:

    mkdir -p ~/.config/pelican
    mkdir -p ~/.local/share/pelican/{logs,volumes,archives,backups}
    mkdir -p ~/.pelican/tmp

    2. Generate SSL certificates

    Wings requires HTTPS. Use mkcert to generate locally-trusted certificates:

    mkcert -install
    mkcert -cert-file ~/.config/pelican/localhost.pem \\n       -key-file ~/.config/pelican/localhost-key.pem \\n       localhost 127.0.0.1

    Wings expects the Docker socket at /var/run/docker.sock, but Docker Desktop uses ~/.docker/run/docker.sock. Create a symlink:

    sudo ln -sf ~/.docker/run/docker.sock /var/run/docker.sock

    4. Configure Wings

    Copy your auto-generated config from the Panel (or use wings configure) to ~/.config/pelican/config.yml. You must modify the paths and system settings to match your macOS environment:

    api:
      ssl:
        enabled: true
        cert: /Users/<you>/.config/pelican/localhost.pem
        key: /Users/<you>/.config/pelican/localhost-key.pem
    system:
      root_directory: /Users/<you>/.local/share/pelican
      log_directory: /Users/<you>/.local/share/pelican/logs
      data: /Users/<you>/.local/share/pelican/volumes
      archive_directory: /Users/<you>/.local/share/pelican/archives
      backup_directory: /Users/<you>/.local/share/pelican/backups
      tmp_directory: /Users/<you>/.pelican/tmp
      user:
        uid: 501    # your UID (run `id -u`)
        gid: 20     # your GID (run `id -g`)
        passwd:
          enable: true
          directory: /Users/<you>/.config/pelican
      machine_id:
        enable: false
      check_permissions_on_boot: false
      enable_log_rotate: false

    Note: Replace <you> with your macOS username.

    Critical macOS Configuration Rules:

    • Use /Users/ paths: Docker Desktop's file sharing only grants the VM access to paths under /Users, /Volumes, /private, and /tmp. Using paths like /var/lib/pelican will cause container bind-mounts to fail.
    • Set uid/gid to your user: This prevents Wings from attempting to use useradd (which is for Linux).
    • machine_id.enable: false: Prevents errors from attempting to bind mount /etc/machine-id (which doesn't exist on macOS).
    • check_permissions_on_boot: false: Prevents Wings from trying to chown directories to a non-existent pelican system user.
    • enable_log_rotate: false: macOS does not use /etc/logrotate.d/.

    5. Panel node configuration

    In the Pelican Panel, configure the node with these settings:

    • FQDN: localhost or 127.0.0.1
    • Port: 8080
    • SSL: enabled
    • Scheme: HTTPS

    6. Start Wings

    Run Wings without sudo. Running as root will create directories that the Docker Desktop VM cannot access, causing container failures.

    ./wings --config ~/.config/pelican/config.yml

    If the Panel displays "is not Pelican Wings!" after startup, clear the Panel cache:

    php artisan cache:clear
    ./wings --config ~/.config/pelican/config.yml
  4. SFTPServer username format requirements

    main
    For security and to prevent bot flooding, usernames must match a specific regex pattern before the API is even queried. The expected format is: (.+)\.([a-z0-9]{8})$ (e.g., username.abcdefgh). If a username does not match this pattern, the connection is rejected with an SftpInvalidCredentialsError.
  5. How the Server struct works

    main

    The Server struct is the central high-level definition for a server instance controlled by Wings. It acts as a coordinator between several subsystems:

    • Configuration: Managed via Configuration, containing build settings, environment variables, and startup commands.
    • Environment: Managed via environment.ProcessEnvironment, which handles the actual process lifecycle, state tracking, and log reading.
    • Filesystem: Accessed via Filesystem(), providing access to the server's isolated data directory.
    • Remote Client: A remote.Client used to push state changes (like PushServerStateChange) and fetch configurations from the Panel.
    • Lifecycle State: Tracks whether the server is installing, transferring, or restoring using atomic booleans to prevent concurrent operations.
  6. Run the Wings API server

    main
    The wings command is the primary entrypoint for running the API server. It allows for programmatic control of game servers for the Pelican Panel. By default, it loads configuration from /etc/pelican/config.yml and starts the API, SFTP, and cron subsystems.
    ./wings
  7. Convert Server to APIResponse for Panel communication

    main

    To provide the Panel with the necessary data to display resource utilization and current state, use ToAPIResponse(). This returns an APIResponse struct containing:

    • State: The current process state.
    • IsSuspended: Whether the server is suspended.
    • Utilization: Current resource usage (CPU, RAM, etc.).
    • Configuration: The server's configuration settings.
    // response is used to send data back to the Panel
    response := server.ToAPIResponse()
  8. Run starts the SFTP server listener

    main

    Starts the SFTP server by:

    1. Checking for or generating an ED25519 private key at the path returned by PrivateKeyPath().
    2. Configuring SSH server settings (Key exchanges, Ciphers, and MACs).
    3. Setting up PasswordCallback and PublicKeyCallback to validate credentials via the Pelican API.
    4. Opening a TCP listener on the configured Listen address.
    5. Entering a loop to accept and handle inbound connections concurrently.
    func (c *SFTPServer) Run() error
  9. AcceptInbound handles an incoming SSH connection

    main

    Processes a single net.Conn by performing the SSH handshake. It validates that the requested channel is a session and specifically the sftp subsystem. If the connection is authenticated, it identifies the target server instance using the uuid extension provided during authentication and spawns a goroutine to Handle the channel.

    func (c *SFTPServer) AcceptInbound(conn net.Conn, config *ssh.ServerConfig) error
  10. New creates a new SFTPServer instance

    main

    Initializes an SFTPServer using the provided *server.Manager. It pulls default settings (Base path, Read-only status, and Listen address) from the global system configuration (config.Get().System).

    func New(m *server.Manager) *SFTPServer
  11. Initialize a new Server instance with New()

    main

    To create a new managed server instance, use the New function. This initializes the server with a background context, default configuration values, and necessary synchronization primitives (like power locks and sink pools). You must provide a remote.Client which is used for communicating with the Pelican Panel.

    Note that New sets up the initial state as environment.ProcessOfflineState.

    import "github.com/pelican/wings/server"
    
    // client is an implementation of remote.Client
    server, err := server.New(client)
    if err != nil {
        // handle error
    }
  12. Get server metadata with ID() and PID()

    main

    Use these methods to retrieve the unique identifiers for a server instance:

    • ID(): Returns the UUID of the server instance.
    • PID(): Returns the Panel Database ID (DBID) for the server instance.
    uuid := server.ID()
    dbID := server.PID()