terminus

repository·main·Indexed 20 days ago

https://github.com/usetrmnl/terminus

A Ruby/Hanami-based web server designed to manage TRMNL devices on local networks or hosted clouds. As a 'Build Your Own Server' (BYOS) implementation, it provides privacy-first control over device stacks and data, featuring API and web interfaces for managing devices, firmware, models, playlists, screens, and extensions.

Tokens
22.9K
Snippets
82
Records
104
Agent score
71%

What's inside terminus

  1. Recommended Raspberry Pi hardware for Terminus

    main

    Terminus has been tested and optimized for the following hardware:

    • Raspberry Pi 5: Recommended for its high performance and feature set.
    • Raspberry Pi Zero 2 W: Suitable for compact form factors where high speed is not a priority.
  2. Firmware Compatibility and Versioning

    main

    Terminus follows the Unbrickable Pledge, ensuring the latest version of Terminus always supports the latest version of TRMNL firmware.

    If you are using older hardware with legacy firmware, you must use a corresponding older version of Terminus.

    Firmware Version Mapping:

    • Firmware 1.6.9 (and lower): Requires Terminus version 0.41.0 or lower.
    • Firmware 1.6.10 (and higher): Requires Terminus version 0.42.0 or higher.
  3. Understand screen content types: HTML, Preprocessed, and Unprocessed URIs

    main

    When using POST or PATCH requests to create or update screens, you can supply different types of content depending on your needs:

    HTML Content

    Allows you to render custom HTML, CSS, and JavaScript as an image on your device.

    • Best Practice: Use * { margin: 0; } in your CSS to prevent white borders around the generated screen.
    • Flexibility: You can externally link assets and use the Terminus firmware link. You can also use the 'Designs' feature in the Terminus UI to build screens in real-time and then supply the result to this endpoint.

    Preprocessed URI

    Allows you to render an image that has already been formatted for the device.

    • Requirement: The preprocessed value must be set to true.
    • Constraint: The image must be fully compatible with the device (greyscale, bit depth, color depth, etc.).
    • Behavior: Terminus does not process this image; it only caches it locally for display.

    Unprocessed URI

    Allows you to provide a standard image that Terminus will process for you.

    • Behavior: Terminus automatically processes the image to ensure it is compatible with the device rendering requirements.
    • Dimensions: You can provide an optional dimensions parameter using ImageMagick geometry syntax. If not supplied, it defaults to 800x480.
  4. Understand HTML/CSS sanitization

    main

    The application uses the Sanitize gem to clean HTML/CSS content across the Console, API, and UI. Sanitization is managed by the Terminus::Sanitizer class.

    By default, it uses the Sanitize::Config::RELAXED configuration but includes additional support for style and source elements. If specific elements are being stripped from your content, it is due to these sanitization rules.

  5. Use Liquid templates in extensions

    main
    Extensions use Liquid templates to transform raw data into HTML for device rendering. This includes standard Liquid functionality and enhanced features provided by the trmnl-liquid gem. Templates allow you to iterate over data sources (e.g., {% for item in source_1 %}) and apply filters like truncate.
  6. Use the Build Matrix for responsive design

    main
    The Build Matrix allows an extension to render responsive designs for multiple device models simultaneously. When building for multiple models, the Liquid template has access to dynamic model attributes, such as {{model.bit_depth}}. This ensures the layout is optimized for the specific hardware being targeted.
  7. Configure extension schedules

    main
    By default, extensions have no schedule. To make an extension automatically update the screens on a device, you must set a schedule. Once scheduled, the generated screens will be updated according to that schedule and can be added to device playlists.
  8. How background jobs work in Terminus

    main

    All background jobs in Terminus are managed by the Sidekiq worker service. While most jobs are triggered via the UI, several core synchronization jobs run automatically at application boot to cache data from the TRMNL server.

    Key synchronization jobs include:

    • Firmware: Downloads latest firmware for automatic device updates (defaults to every 6 hours).
    • Fonts: Synchronizes TRMNL fonts as public assets (defaults to once a day).
    • Model: Synchronizes TRMNL palettes and models (defaults to once a day).
    • Sensor: Synchronizes hosted (e.g., Raspberry Pi) sensor data for screen rendering (defaults to every minute).

    The specific cron schedules and configurations for these jobs are defined in config/sidekiq_scheduler.yml.

  9. Manage Users and Registration

    main

    Initial Registration

    When launching Terminus for the first time, there are no users. The first user to register is automatically granted Verified status and full system access. All subsequent users are marked as Unverified and must be manually verified by an administrator to gain access.

    User Settings

    Once logged in, users can manage their own profiles via the following paths:

    • Email: /me/login
    • Password: /me/password
    • Remember Me: /me/remember (to enable, disable, or forget session persistence)
  10. Deploy Terminus Application and Sidekiq Worker

    main

    The main Terminus deployment uses a single Pod containing two containers:

    1. terminus: The web application.
    2. sidekiq: The background job processor.

    Both containers must share the same volume for uploads and use the Recreate deployment strategy because the PVC is ReadWriteOnce.

    Key Environment Variables:

    • APP_SETUP: Set to true.
    • API_URI: Your actual public domain.
    • DATABASE_URL: Sourced from terminus-secrets.
    • APP_SECRET: Sourced from terminus-secrets.
    • KEYVALUE_URL: The connection string for Valkey (e.g., redis://terminus-valkey:6379).
    • RACK_ATTACK_ALLOWED_SUBNETS: Sourced from terminus-config.

    Permissions: The container runs as UID 1000, so fsGroup: 1000 must be set in the securityContext to ensure correct file permissions on the shared volume.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: terminus
    spec:
      replicas: 1
      strategy:
        type: Recreate
      selector:
        matchLabels:
          app.kubernetes.io/name: terminus
      template:
        metadata:
          labels:
            app.kubernetes.io/name: terminus
        spec:
          securityContext:
            fsGroup: 1000
          containers:
          - name: terminus
            image: ghcr.io/usetrmnl/terminus:latest
            ports:
            - containerPort: 2345
              name: http
            volumeMounts:
            - mountPath: /app/public/uploads
              name: data
            env:
            - name: APP_SETUP
              value: "true"
            - name: API_URI
              value: https://terminus.example.com
            - name: KEYVALUE_URL
              value: redis://terminus-valkey:6379
            # ... other env vars from secrets/configmaps
          - name: sidekiq
            image: ghcr.io/usetrmnl/terminus:latest
            command: ["bundle", "exec", "sidekiq", "-r", "./config/sidekiq.rb"]
            volumeMounts:
            - mountPath: /app/public/uploads
              name: data
            env:
            - name: API_URI
              value: https://terminus.example.com
            - name: KEYVALUE_URL
              value: redis://terminus-valkey:6379
            # ... other env vars from secrets/configmaps
          volumes:
          - name: data
            persistentVolumeClaim:
              claimName: terminus-uploads
  11. Manage Terminus Secrets

    main

    Sensitive configuration must be stored in a Kubernetes Secret. Never commit secrets to git!

    Required keys:

    • DATABASE_URL: Your PostgreSQL connection string.
    • APP_SECRET: A secure key. Generate this using openssl rand -hex 32.

    For production, use kubectl to create the secret directly to avoid plain-text files.

    # Recommended production method
    kubectl create secret generic terminus-secrets \
      --from-literal=DATABASE_URL='postgres://user:password@host:5432/terminus' \
      --from-literal=APP_SECRET="$(openssl rand -hex 32)"
  12. Quick Start with Docker

    main

    To immediately spin up Terminus on your local machine using Docker for exploration, run the following command.

    ⚠️ Warning: This script is not idempotent. Running it more than once will result in different database credentials each time. It is intended for quick exploration, not permanent use. For permanent setups, refer to the Docker documentation.

    Once launched, access the system at http://localhost:2300 and click the Register link to create your first account.

    curl https://raw.githubusercontent.com/usetrmnl/terminus/refs/heads/main/scripts/docker/quick.sh | bash