1
Architecture
Pavel Baksy edited this page 2026-01-13 17:08:01 +01:00

Architecture

Technical overview of Roster's architecture.

Technology Stack

Core Technologies

  • GTK 4 - UI toolkit
  • libadwaita - GNOME design patterns and widgets
  • Python 3 - Application logic
  • libsoup3 - HTTP client (from GNOME Platform)
  • libsecret - Credential storage via GNOME Keyring
  • GJS - JavaScript runtime for scripts

Build System

  • Meson - Build configuration
  • GResources - Bundle UI files and assets

Application Structure

Main Components

RosterApplication (main.py)

  • Singleton application class inheriting from Adw.Application
  • Manages application lifecycle
  • Handles global actions (quit, about, preferences)
  • Creates and presents main window

RosterWindow (window.py)

  • Main application window inheriting from Adw.ApplicationWindow
  • GTK template class with UI from window.ui
  • Manages tabs, requests, and responses
  • Coordinates HTTP operations

UI Architecture

Template-based UI:

  • UI defined in XML files (GTK Builder format)
  • Loaded via GResources at runtime
  • @Gtk.Template decorator binds Python class to UI
  • Gtk.Template.Child() accesses widgets from template

Resource Management:

  • UI files defined in src/roster.gresource.xml
  • Compiled into binary bundle during build
  • Base path: /cz/bugsy/roster

Data Flow

HTTP Request Flow

User Input → Request Configuration → Variable Substitution →
Preprocessing Script → HTTP Request (libsoup3) → Response →
Postprocessing Script → Display Response → Save to History

Variable Substitution

  1. User enters {{variable_name}} in request
  2. Current environment selected from dropdown
  3. Variable value retrieved (from JSON or Keyring)
  4. Placeholder replaced with actual value
  5. Request sent with substituted values

Script Execution

Preprocessing:

  1. Load script from request
  2. Create JavaScript context (GJS)
  3. Inject request, roster, console objects
  4. Execute script synchronously
  5. Apply modifications to request object

Postprocessing:

  1. Load script from request
  2. Create JavaScript context (GJS)
  3. Inject response, roster, console objects
  4. Execute script synchronously
  5. Variables updated via roster.setVariable()

Data Storage

File Locations

Native:

~/.local/share/cz.bugsy.roster/
├── requests.json    # Projects, environments, regular variables
└── session.json     # History, window state

Flatpak:

~/.var/app/cz.bugsy.roster/data/cz.bugsy.roster/
├── requests.json
└── session.json

Data Format

requests.json:

{
  "projects": [
    {
      "id": "uuid",
      "name": "Project Name",
      "icon": "icon-name",
      "environments": [
        {
          "id": "uuid",
          "name": "Production",
          "variables": {
            "base_url": "https://api.example.com",
            "api_key": ""  // Empty if sensitive
          }
        }
      ],
      "requests": [
        {
          "id": "uuid",
          "name": "Get Users",
          "method": "GET",
          "url": "{{base_url}}/users",
          "headers": {},
          "body": "",
          "preprocessing_script": "",
          "postprocessing_script": ""
        }
      ]
    }
  ]
}

Sensitive Variables

Stored in GNOME Keyring with schema:

  • Schema: cz.bugsy.roster.EnvironmentVariable
  • Attributes: project_id, environment_id, variable_name
  • Secret: Variable value (encrypted)

Key Design Patterns

Singleton Application

Single instance ensured by Adw.Application. Attempting to launch a second instance activates the existing window.

Template Classes

UI components use GTK template pattern:

@Gtk.Template(resource_path='/cz/bugsy/roster/window.ui')
class RosterWindow(Adw.ApplicationWindow):
    __gtype_name__ = 'RosterWindow'

    url_entry = Gtk.Template.Child()
    method_dropdown = Gtk.Template.Child()

Actions

Application and window actions follow GNOME conventions:

action = Gio.SimpleAction.new("quit", None)
action.connect("activate", self.on_quit)
self.add_action(action)
self.set_accels_for_action("app.quit", ["<Ctrl>Q"])

HTTP Client

Uses libsoup3 (from GNOME Platform):

  • Asynchronous HTTP requests
  • Automatic redirect handling
  • Cookie management
  • SSL/TLS support
  • HTTP/2 support

Internationalization

Uses gettext for translations:

  • Domain: "roster"
  • Template files (.in) processed by Meson
  • i18n.merge_file() generates localized files
  • Translations in po/ directory

Security Model

Sandboxing (Flatpak)

Permissions required:

  • --share=network - HTTP requests
  • --talk-name=org.freedesktop.secrets - GNOME Keyring access
  • --filesystem=xdg-data/cz.bugsy.roster - Data directory

Script Sandboxing

Scripts run in isolated GJS context:

  • No file system access
  • No network access (except main request)
  • No process execution
  • Only whitelisted APIs available

Next Steps