Add comprehensive wiki documentation

Pavel Baksy 2026-01-12 23:50:13 +01:00
parent 3da66bb11f
commit 72a00e5cd6
10 changed files with 3048 additions and 1 deletions

622
API-Reference.md Normal file

@ -0,0 +1,622 @@
# API Reference
Complete JavaScript API reference for Roster preprocessing and postprocessing scripts.
## Overview
Roster provides a JavaScript API accessible from [[Scripts]] for automating request modifications and response processing.
**Language:** JavaScript (ES5+)
**Runtime:** GJS (GNOME JavaScript)
**Context:** Sandboxed, synchronous execution
## Global Objects
### Preprocessing Script Context
Available in preprocessing scripts only:
```javascript
request // Modifiable request object
roster // Roster API (with getVariable)
console // Console output
```
### Postprocessing Script Context
Available in postprocessing scripts only:
```javascript
response // Read-only response object
roster // Roster API (without getVariable)
console // Console output
```
## Request Object
**Available in:** Preprocessing scripts only
**Type:** Mutable
### Properties
#### `request.method`
**Type:** `string`
**Values:** `"GET"`, `"POST"`, `"PUT"`, `"DELETE"`
**Description:** HTTP method for the request
**Example:**
```javascript
// Change method from GET to POST
request.method = "POST";
console.log('Method changed to:', request.method);
```
#### `request.url`
**Type:** `string`
**Description:** Complete URL for the request
**Example:**
```javascript
// Add query parameter
request.url = request.url + '?timestamp=' + Date.now();
// Replace placeholder
const userId = roster.getVariable('user_id');
request.url = request.url.replace('{userId}', userId);
// Change domain
request.url = request.url.replace('localhost', 'api.example.com');
```
#### `request.headers`
**Type:** `object`
**Description:** HTTP headers as key-value pairs
**Example:**
```javascript
// Add header
request.headers['Authorization'] = 'Bearer ' + token;
// Modify existing header
request.headers['Content-Type'] = 'application/json';
// Delete header
delete request.headers['X-Old-Header'];
// Read header
const contentType = request.headers['Content-Type'];
```
#### `request.body`
**Type:** `string`
**Description:** Request body content
**Example:**
```javascript
// Set body
request.body = JSON.stringify({ name: "John", age: 30 });
// Modify existing body
const data = JSON.parse(request.body);
data.timestamp = new Date().toISOString();
request.body = JSON.stringify(data);
// Clear body
request.body = "";
```
## Response Object
**Available in:** Postprocessing scripts only
**Type:** Immutable (read-only)
### Properties
#### `response.body`
**Type:** `string`
**Description:** Response body content
**Example:**
```javascript
// Parse JSON response
const data = JSON.parse(response.body);
console.log('User name:', data.name);
// Check if response contains text
if (response.body.includes('error')) {
console.error('Response contains error');
}
// Get response length
console.log('Response size:', response.body.length, 'bytes');
```
#### `response.headers`
**Type:** `object`
**Description:** Response headers as key-value pairs (read-only)
**Example:**
```javascript
// Read header
const contentType = response.headers['Content-Type'];
console.log('Content type:', contentType);
// Check if header exists
if (response.headers['X-Rate-Limit-Remaining']) {
const remaining = response.headers['X-Rate-Limit-Remaining'];
console.log('Rate limit remaining:', remaining);
}
// List all headers
for (const key in response.headers) {
console.log(key + ':', response.headers[key]);
}
```
#### `response.statusCode`
**Type:** `number`
**Description:** HTTP status code (200, 404, 500, etc.)
**Example:**
```javascript
// Check if successful
if (response.statusCode === 200) {
console.log('Success!');
} else if (response.statusCode >= 400 && response.statusCode < 500) {
console.error('Client error:', response.statusCode);
} else if (response.statusCode >= 500) {
console.error('Server error:', response.statusCode);
}
// Handle specific status codes
switch (response.statusCode) {
case 200:
console.log('OK');
break;
case 201:
console.log('Created');
break;
case 401:
console.error('Unauthorized');
break;
case 404:
console.error('Not found');
break;
default:
console.log('Status:', response.statusCode);
}
```
#### `response.statusText`
**Type:** `string`
**Description:** HTTP status text ("OK", "Not Found", etc.)
**Example:**
```javascript
console.log('Response:', response.statusCode, response.statusText);
// Output: "Response: 200 OK"
if (response.statusText === "OK") {
// Process successful response
}
```
#### `response.responseTime`
**Type:** `number`
**Description:** Response time in milliseconds
**Example:**
```javascript
console.log('Response took', response.responseTime, 'ms');
if (response.responseTime > 1000) {
console.warn('Slow response:', response.responseTime, 'ms');
}
// Store for monitoring
roster.setVariable('last_response_time', response.responseTime.toString());
```
## Roster API
**Available in:** Both preprocessing and postprocessing scripts
**Namespace:** `roster`
### Methods
#### `roster.getVariable(name)`
**Available in:** Preprocessing scripts only
**Returns:** `string` or `undefined`
**Description:** Get variable value from selected environment
**Parameters:**
- `name` (string): Variable name
**Example:**
```javascript
// Get variable
const apiKey = roster.getVariable('api_key');
if (apiKey) {
request.headers['X-API-Key'] = apiKey;
} else {
console.error('API key not defined');
}
// Get with default value
const timeout = roster.getVariable('timeout') || '30';
console.log('Using timeout:', timeout);
```
**Important Notes:**
- Returns `undefined` if variable doesn't exist
- Gets value from currently selected environment
- Can access both regular and [[Sensitive-Variables|sensitive variables]]
- Sensitive variable values are automatically decrypted from keyring
#### `roster.setVariable(name, value)`
**Available in:** Both preprocessing and postprocessing scripts
**Returns:** `undefined`
**Description:** Set or update variable in selected environment
**Parameters:**
- `name` (string): Variable name (must match `/^\w+$/`)
- `value` (string): Variable value
**Example:**
```javascript
// Set variable
roster.setVariable('user_id', '12345');
console.log('Saved user ID');
// Update existing variable
const token = data.access_token;
roster.setVariable('auth_token', token);
// Create new variable (auto-created if doesn't exist)
roster.setVariable('session_id', generateSessionId());
```
**Important Notes:**
- Creates variable if it doesn't exist
- Updates value in currently selected environment
- Variable name must be alphanumeric + underscore
- If variable is marked as sensitive, value goes to keyring automatically
- Value is converted to string
#### `roster.setVariables(object)`
**Available in:** Both preprocessing and postprocessing scripts
**Returns:** `undefined`
**Description:** Set or update multiple variables at once (batch operation)
**Parameters:**
- `object` (object): Key-value pairs of variable names and values
**Example:**
```javascript
// Set multiple variables
roster.setVariables({
user_id: data.user.id,
user_name: data.user.name,
user_email: data.user.email,
session_id: data.session_id
});
// Extract response data
const data = JSON.parse(response.body);
roster.setVariables({
access_token: data.access_token,
refresh_token: data.refresh_token,
expires_at: data.expires_at
});
console.log('Saved authentication tokens');
```
**Important Notes:**
- All variables updated in single operation
- More efficient than multiple `setVariable()` calls
- Same validation rules as `setVariable()`
- Sensitive variables automatically routed to keyring
### Properties
#### `roster.project.name`
**Available in:** Preprocessing scripts only
**Type:** `string`
**Description:** Current project name
**Example:**
```javascript
const projectName = roster.project.name;
console.log('Project:', projectName);
// Add project name to request
request.headers['X-Project'] = projectName;
```
#### `roster.project.environments`
**Available in:** Preprocessing scripts only
**Type:** `string[]` (array of strings)
**Description:** Array of environment names in current project
**Example:**
```javascript
const envs = roster.project.environments;
console.log('Available environments:', envs.join(', '));
// Output: "Available environments: Production, Staging, Development"
// Check if environment exists
if (envs.includes('Production')) {
console.log('Production environment available');
}
// Log count
console.log('Environment count:', envs.length);
```
## Console API
**Available in:** Both preprocessing and postprocessing scripts
**Namespace:** `console`
### Methods
#### `console.log(...args)`
**Returns:** `undefined`
**Description:** Print message to script output
**Example:**
```javascript
console.log('Simple message');
console.log('User ID:', userId);
console.log('Request sent to', request.url);
console.log('Response status:', response.statusCode, response.statusText);
// Multiple arguments
const name = 'John';
const age = 30;
console.log('User:', name, 'Age:', age);
```
**Output location:**
- Preprocessing: "Preprocessing" tab in request panel
- Postprocessing: "Postprocessing" tab in request panel
#### `console.error(...args)`
**Returns:** `undefined`
**Description:** Print error message to script output
**Example:**
```javascript
if (response.statusCode !== 200) {
console.error('Request failed with status:', response.statusCode);
}
if (!data.access_token) {
console.error('No access token in response');
}
try {
const data = JSON.parse(response.body);
} catch (e) {
console.error('JSON parse error:', e.message);
}
```
**Visual difference:**
- May be styled differently than `console.log()` in output
- Indicates errors or warnings
## Built-in JavaScript Objects
Standard JavaScript objects available in GJS:
### Date
```javascript
// Current timestamp
const now = new Date();
console.log('Current time:', now.toISOString());
// Unix timestamp
const timestamp = Date.now();
console.log('Timestamp:', timestamp);
// Date arithmetic
const expiresAt = new Date(Date.now() + 3600000); // +1 hour
roster.setVariable('token_expires', expiresAt.toISOString());
```
### JSON
```javascript
// Parse JSON
const data = JSON.parse(response.body);
console.log('Parsed data:', data.name);
// Stringify JSON
const body = { name: "John", age: 30 };
request.body = JSON.stringify(body);
// Pretty print
request.body = JSON.stringify(body, null, 2);
```
### Math
```javascript
// Random number
const requestId = Math.floor(Math.random() * 1000000);
roster.setVariable('request_id', requestId.toString());
// Rounding
const responseTimeSeconds = Math.round(response.responseTime / 1000);
console.log('Response time:', responseTimeSeconds, 'seconds');
```
### String
```javascript
// String manipulation
const url = request.url.replace('http://', 'https://');
const uppercase = data.name.toUpperCase();
const trimmed = data.description.trim();
// String methods
if (response.body.includes('error')) {
console.error('Response contains error');
}
const parts = request.url.split('/');
const lastPart = parts[parts.length - 1];
```
### Array
```javascript
// Array methods
const data = JSON.parse(response.body);
const itemIds = data.items.map(item => item.id);
console.log('Item IDs:', itemIds.join(', '));
// Filter
const active = data.items.filter(item => item.active);
console.log('Active items:', active.length);
// Find
const firstItem = data.items.find(item => item.id === '123');
if (firstItem) {
console.log('Found item:', firstItem.name);
}
```
### Object
```javascript
// Object methods
const headers = Object.keys(response.headers);
console.log('Header count:', headers.length);
// Merge objects
const defaults = { timeout: 30, retries: 3 };
const config = Object.assign({}, defaults, userConfig);
// Check property
if (data.hasOwnProperty('access_token')) {
roster.setVariable('auth_token', data.access_token);
}
```
## Error Handling
Scripts should handle errors gracefully:
```javascript
// Try-catch for parsing
try {
const data = JSON.parse(response.body);
roster.setVariable('user_id', data.user.id);
} catch (e) {
console.error('Failed to parse JSON:', e.message);
console.error('Response body:', response.body);
}
// Check before accessing
if (data && data.user && data.user.id) {
roster.setVariable('user_id', data.user.id);
} else {
console.error('Invalid response structure');
}
// Validate response status
if (response.statusCode === 200) {
const data = JSON.parse(response.body);
// Process data
} else {
console.error('Request failed:', response.statusCode);
}
```
## Limitations
### No Asynchronous Operations
- No `setTimeout` / `setInterval`
- No `Promise` / `async` / `await`
- No `fetch()` / `XMLHttpRequest`
- Scripts execute synchronously
### No External Libraries
- Cannot import npm packages
- Cannot `require()` external modules
- Only built-in JavaScript objects available
### No File System Access
- Cannot read/write files
- Cannot execute shell commands
- Cannot access environment variables (use Roster variables instead)
### No Network Access
- Cannot make additional HTTP requests
- Use Roster's request system instead
## Type Reference
### Variable Name Pattern
Variable names must match: `/^\w+$/`
Valid:
- `api_key`
- `user_id`
- `access_token`
- `BASE_URL`
- `token123`
Invalid:
- `api-key` (hyphen not allowed)
- `api.key` (dot not allowed)
- `api key` (space not allowed)
- `@token` (special character)
### Type Conversions
Variables are always stored as strings:
```javascript
// Numbers must be converted
roster.setVariable('count', data.count.toString());
// Read as number
const count = parseInt(roster.getVariable('count'), 10);
// Booleans must be converted
roster.setVariable('is_active', data.active.toString());
// Read as boolean
const isActive = roster.getVariable('is_active') === 'true';
```
## Next Steps
- [[Scripts]] - Practical examples and workflows
- [[Variables]] - Learn about environment variables
- [[Sensitive-Variables]] - Secure credential storage

260
FAQ.md Normal file

@ -0,0 +1,260 @@
# Frequently Asked Questions
## General
### What is Roster?
Roster is a native GNOME HTTP client for testing and debugging APIs. It provides a clean interface for sending HTTP requests, viewing responses, and automating workflows.
### Is Roster free?
Yes! Roster is free and open-source software licensed under GPL-3.0-or-later.
### What platforms does Roster support?
Roster is designed for the GNOME desktop environment on Linux. It works on any Linux distribution with GNOME 40+.
### Can I use Roster on Windows or macOS?
Roster is specifically built for GNOME/Linux and is not currently available for Windows or macOS.
## Installation
### How do I install Roster?
See [[Installation]] for complete instructions. You can:
- Build from source
- Install via Flatpak
- Use GNOME Builder
### Will Roster be on Flathub?
Support for Flathub is planned for future releases.
### What dependencies does Roster need?
Runtime: GTK 4, libadwaita 1, Python 3, libsoup3, libsecret, GJS
See [[Installation]] for complete dependency list.
## Variables and Environments
### What's the difference between regular and sensitive variables?
**Regular variables** are stored in plain text JSON files. Use for non-sensitive data like URLs.
**Sensitive variables** are encrypted and stored in GNOME Keyring. Use for API keys, passwords, and tokens.
See [[Sensitive-Variables]] for details.
### Can I use variables in the request body?
Yes! Use `{{variable_name}}` syntax in:
- URL
- Headers
- Request body
### How do I switch between environments?
Use the environment dropdown at the top of the request panel. Switching environments updates all variable values.
### Can variables from one project be used in another?
No. Variables are scoped to projects and cannot be shared between projects.
## Security
### Is it safe to store API keys in Roster?
**If you use sensitive variables: Yes.**
Mark variables as sensitive (click the lock icon), and values are encrypted in GNOME Keyring.
**If you use regular variables: No.**
Regular variables are stored in plain text. Always mark secrets as sensitive!
### Where are my secrets stored?
Sensitive variables are stored in GNOME Keyring at:
- Native: `~/.local/share/keyrings/`
- Encrypted with your login password
- Same security as browser passwords, WiFi credentials
### Can I view my keyring secrets?
Yes! Open "Passwords and Keys" application (Seahorse), navigate to "Login" keyring, and look for entries starting with "Roster:".
### What happens if I forget to mark a variable as sensitive?
You can click the lock icon at any time to mark it as sensitive. Values will be moved from JSON to keyring automatically.
## Scripts
### What language do scripts use?
JavaScript (ES5+), executed via GJS (GNOME JavaScript runtime).
### Can I use npm packages in scripts?
No. Scripts run in a sandboxed environment without access to external libraries or npm packages.
### Can scripts access the file system?
No. Scripts cannot read/write files, execute shell commands, or access system resources.
### Why can't I use async/await?
Scripts execute synchronously. No async operations (setTimeout, Promises, async/await, fetch) are available.
### Can I make HTTP requests from scripts?
No. Use Roster's request system and chain requests using variables instead.
See [[Scripts]] for complete documentation.
## Requests and Responses
### Where is request history stored?
**Native:** `~/.local/share/cz.bugsy.roster/session.json`
**Flatpak:** `~/.var/app/cz.bugsy.roster/data/cz.bugsy.roster/session.json`
### How long is history kept?
History persists across sessions until you clear it manually.
### Can I export requests?
Yes! Click the export button and choose a format:
- cURL
- More formats may be added in future versions
### Does Roster support file uploads?
Multipart form uploads are not currently supported. Planned for future releases.
### Can I test WebSocket connections?
WebSocket support is not currently available.
## Troubleshooting
### Roster won't start
**Check dependencies:**
```bash
# Verify GTK 4 and libadwaita are installed
pkg-config --modversion gtk4 libadwaita-1
```
**Check logs:**
```bash
# Native
roster
# Flatpak
flatpak run cz.bugsy.roster
```
### Cannot access GNOME Keyring
**Ensure keyring is unlocked:**
1. Open "Passwords and Keys" (Seahorse)
2. Right-click "Login" keyring
3. Select "Unlock"
**For Flatpak, check permissions:**
```bash
flatpak info --show-permissions cz.bugsy.roster
# Should include: --talk-name=org.freedesktop.secrets
```
### Variables not substituting
**Check:**
1. Environment is selected (dropdown at top of request)
2. Variable names match exactly (case-sensitive)
3. Syntax is correct: `{{variable_name}}`
4. Variable is defined in selected environment
### Scripts not executing
**Check:**
1. Request is saved (scripts only work on saved requests)
2. Script has no syntax errors
3. Check script output tabs for errors
4. GJS (gjs package) is installed
### Request failing with SSL errors
**For self-signed certificates:**
Roster uses system SSL certificates. Add your certificate to the system trust store or use development environments without SSL validation.
## Data and Privacy
### Does Roster collect any data?
No. Roster does not collect, transmit, or share any data. All information stays on your local machine.
### Can I backup my projects?
Yes! Backup these files:
- `~/.local/share/cz.bugsy.roster/requests.json` (projects and variables)
- `~/.local/share/keyrings/` (sensitive variables)
### How do I reset Roster?
Delete the data directory:
```bash
# Native
rm -rf ~/.local/share/cz.bugsy.roster/
# Flatpak
rm -rf ~/.var/app/cz.bugsy.roster/data/cz.bugsy.roster/
```
**Warning:** This deletes all projects, requests, and history!
Sensitive variables in keyring must be deleted separately via Seahorse.
## Contributing
### How can I contribute?
Contributions welcome! See [[Contributing]] for guidelines.
### Where do I report bugs?
Report issues at: https://git.bugsy.cz/beval/roster/issues
### Can I request features?
Yes! Open an issue with the "enhancement" label.
## Comparison
### How is Roster different from Postman?
- **Roster**: Native GNOME app, lightweight, open-source, Linux-only
- **Postman**: Cross-platform, Electron-based, cloud sync, team features
Roster focuses on being a simple, fast, native GNOME experience.
### How is Roster different from Insomnia?
Similar to Postman comparison. Roster is native GNOME with no cloud dependencies.
### How is Roster different from HTTPie?
- **Roster**: GUI application with project management
- **HTTPie**: Command-line tool
Roster provides a visual interface while HTTPie is CLI-focused.
## Still Have Questions?
- Check the [[Home|Wiki]] for more documentation
- Open an issue: https://git.bugsy.cz/beval/roster/issues
- Read the source: https://git.bugsy.cz/beval/roster

245
Getting-Started.md Normal file

@ -0,0 +1,245 @@
# Getting Started
This guide walks you through sending your first HTTP request with Roster.
## Launch Roster
After [[Installation]], launch Roster from:
- Your application menu (search for "Roster")
- Command line: `roster` (or `flatpak run cz.bugsy.roster` for Flatpak)
## Your First Request
### Step 1: Create a Project
Projects help organize related requests (e.g., "GitHub API", "My App API").
1. Click the **hamburger menu** (three lines) in the top-left
2. Select **"New Project"**
3. Enter a name: `My First Project`
4. Click **"Create"**
### Step 2: Create a Request
1. Click the **"+"** button in the header bar
2. Or: **Ctrl+T** keyboard shortcut
A new request tab appears with default values.
### Step 3: Configure the Request
**Set the URL:**
```
https://api.github.com/users/octocat
```
**Set the Method:**
- Use the dropdown menu
- Default is `GET` (perfect for this example)
**Add Headers (Optional):**
1. Click the **"Headers"** tab
2. Add a header:
- Key: `User-Agent`
- Value: `Roster/0.5.0`
### Step 4: Send the Request
Click the **"Send"** button (or press **Ctrl+Return**)
### Step 5: View the Response
The response appears in the right panel:
**Status Line:**
```
200 OK (123 ms, 1.2 KB)
```
**Response Headers:**
Click the **"Headers"** dropdown to view all response headers.
**Response Body:**
The JSON response from GitHub API showing user information.
## Saving Requests
### Save for Later
1. Click the **"Save"** button in the header
2. Enter a name: `Get GitHub User`
3. Click **"Save"**
The request is now saved to your project.
### Access Saved Requests
1. Click the **hamburger menu**
2. Your project shows saved requests
3. Click a request to open it in a new tab
## Request History
Every request you send is automatically saved to history.
**View History:**
1. Click the **"History"** button in the header bar
2. Browse past requests
3. Click any entry to open it in a new tab
**History includes:**
- Request details (URL, method, headers, body)
- Response details (status, headers, body, timing)
- Timestamp
## Working with Tabs
### Multiple Tabs
Open multiple requests simultaneously:
- **Ctrl+T** - New request tab
- **Ctrl+W** - Close current tab
- **Ctrl+Tab** / **Ctrl+Shift+Tab** - Switch between tabs
### Modified Indicator
Unsaved changes are marked with a dot (•) on the tab label.
## Next Example: POST Request
Let's send some data to an API.
### Step 1: New Request
Create a new request tab (**Ctrl+T**)
### Step 2: Configure POST Request
**URL:**
```
https://httpbin.org/post
```
**Method:**
```
POST
```
**Headers:**
```
Content-Type: application/json
```
**Body:**
1. Click the **"Body"** tab
2. Select **"JSON"** syntax highlighting
3. Enter JSON data:
```json
{
"name": "Test User",
"email": "test@example.com"
}
```
### Step 3: Send and View
Click **"Send"** and view the response. httpbin.org echoes back your request data.
## Common Request Types
### GET Request
```
Method: GET
URL: https://api.example.com/users
Headers: (optional)
Body: (none)
```
### POST Request
```
Method: POST
URL: https://api.example.com/users
Headers: Content-Type: application/json
Body: {"name": "John", "email": "john@example.com"}
```
### PUT Request
```
Method: PUT
URL: https://api.example.com/users/123
Headers: Content-Type: application/json
Body: {"name": "John Updated"}
```
### DELETE Request
```
Method: DELETE
URL: https://api.example.com/users/123
Headers: (optional)
Body: (none)
```
## Response Features
### Syntax Highlighting
Roster automatically detects and highlights:
- **JSON** responses
- **XML** responses
- **HTML** responses
### Response Timing
View how long the request took:
```
200 OK (1.23 seconds, 5.4 KB)
```
### Response Size
The status line shows the total response size including headers.
## Keyboard Shortcuts
Essential shortcuts to speed up your workflow:
| Action | Shortcut |
|--------|----------|
| New request | **Ctrl+T** |
| Send request | **Ctrl+Return** |
| Close tab | **Ctrl+W** |
| Save request | **Ctrl+S** |
| Switch tabs | **Ctrl+Tab** |
| Quit | **Ctrl+Q** |
See [[Keyboard-Shortcuts]] for the complete list.
## Next Steps
Now that you know the basics, explore more advanced features:
- [[Projects-and-Environments]] - Organize requests and manage environments
- [[Variables]] - Use variables to avoid repetition
- [[Sensitive-Variables]] - Store API keys securely
- [[Scripts]] - Automate workflows with JavaScript
- [[Export]] - Export requests to cURL and other formats
## Tips
**Tip 1: Use httpbin.org for testing**
- `https://httpbin.org/get` - Test GET requests
- `https://httpbin.org/post` - Test POST requests
- `https://httpbin.org/headers` - See your request headers
- `https://httpbin.org/delay/3` - Test timeouts
**Tip 2: Save frequently used requests**
- Save requests to projects for quick access
- Use descriptive names: "Login", "Get User Profile", etc.
**Tip 3: Use syntax highlighting**
- Select JSON/XML/RAW in the body dropdown
- Makes reading requests and responses much easier
**Tip 4: Check the history**
- History persists across sessions
- Great for debugging or comparing responses

114
Home.md

@ -1 +1,113 @@
Welcome to the Wiki.
# Roster Wiki
Welcome to the **Roster** documentation! Roster is a modern HTTP client for GNOME, built with GTK 4 and libadwaita.
## What is Roster?
Roster is a native GNOME application for testing and debugging HTTP APIs. It provides a clean, intuitive interface for:
- HTTP Requests - Send GET, POST, PUT, DELETE requests
- Custom Headers & Bodies - Full control over request configuration
- Response Viewing - View headers, body, status, and timing
- Request History - Persistent history with full request/response data
- Project Organization - Organize requests into projects
- Environment Variables - Manage different environments (dev, staging, prod)
- Sensitive Variables - Secure storage of API keys and tokens in GNOME Keyring
- JavaScript Scripts - Automate workflows with preprocessing/postprocessing
- Export - Export requests to cURL and other formats
- Beautiful UI - Native GNOME experience with libadwaita
## Quick Links
### Getting Started
- [[Installation]] - How to build and install Roster
- [[Getting-Started]] - Your first HTTP request
- [[Projects-and-Environments]] - Organize your work
### Features
- [[Variables]] - Use variables in requests
- [[Sensitive-Variables]] - Secure storage with GNOME Keyring
- [[Scripts]] - Preprocessing and postprocessing automation
- [[Export]] - Export requests to other tools
- [[History]] - Track and replay requests
### Reference
- [[API-Reference]] - Complete JavaScript API documentation
- [[Keyboard-Shortcuts]] - Speed up your workflow
- [[FAQ]] - Frequently asked questions
### Development
- [[Contributing]] - How to contribute to Roster
- [[Development]] - Developer documentation
- [[Architecture]] - Technical overview
## Key Features
### Secure Credential Storage
Store API keys, tokens, and passwords securely in GNOME Keyring with one-click encryption:
```
Variables:
base_url (unlocked) → Stored in JSON (plain text)
api_key (locked) → Stored in GNOME Keyring (encrypted)
```
Learn more: [[Sensitive-Variables]]
### Powerful Automation
Use JavaScript preprocessing and postprocessing scripts to:
- Extract authentication tokens from responses
- Add dynamic headers (timestamps, signatures)
- Chain requests together
- Validate responses
```javascript
// Postprocessing: Extract token from login response
const data = JSON.parse(response.body);
roster.setVariable('auth_token', data.access_token);
console.log('Logged in successfully!');
```
Learn more: [[Scripts]]
### Multi-Environment Support
Manage multiple environments with different variable values:
| Variable | Production | Development |
|----------|-----------|-------------|
| base_url | `api.example.com` | `localhost:3000` |
| api_key | `prod-key-***` | `dev-key-***` |
Switch environments with one click - all variables update automatically.
Learn more: [[Variables]]
## Platform
Roster is built specifically for the GNOME desktop using:
- **GTK 4** - Modern UI toolkit
- **libadwaita** - GNOME design patterns
- **libsoup3** - HTTP networking (from GNOME Platform)
- **libsecret** - Secure credential storage
- **GJS** - JavaScript runtime for scripts
## License
Roster is free and open-source software licensed under **GPL-3.0-or-later**.
## Support
- **Issues**: https://git.bugsy.cz/beval/roster/issues
- **Source**: https://git.bugsy.cz/beval/roster
- **Wiki**: You are here!
## Next Steps
**New to Roster?** Start with [[Installation]] and [[Getting-Started]]
**Need to store API keys?** Check out [[Sensitive-Variables]]
**Want to automate workflows?** Learn about [[Scripts]]

213
Installation.md Normal file

@ -0,0 +1,213 @@
# Installation
This guide covers how to build and install Roster from source.
## Dependencies
Roster requires the following dependencies:
### Runtime Dependencies
- **GTK 4** (>= 4.0)
- **libadwaita 1** (>= 1.0)
- **Python 3** (>= 3.8)
- **libsoup3** - HTTP client library (provided by GNOME Platform)
- **libsecret** - Secure credential storage (provided by GNOME Platform)
- **GJS** - GNOME JavaScript runtime for script execution
### Build Dependencies
- **Meson** (>= 1.0.0)
- **Ninja**
- **pkg-config**
- **gettext** - For internationalization
- **glib-compile-schemas**
- **glib-compile-resources**
## Installation Methods
### Method 1: Build from Source (Native)
This method builds and installs Roster directly on your system.
#### Step 1: Install Dependencies
**Fedora:**
```bash
sudo dnf install meson ninja-build gtk4-devel libadwaita-devel \
libsoup3-devel libsecret-devel gjs python3 \
gettext desktop-file-utils appstream
```
**Ubuntu/Debian:**
```bash
sudo apt install meson ninja-build libgtk-4-dev libadwaita-1-dev \
libsoup-3.0-dev libsecret-1-dev gjs python3 \
gettext desktop-file-utils appstream
```
**Arch Linux:**
```bash
sudo pacman -S meson ninja gtk4 libadwaita libsoup3 libsecret gjs python3 gettext
```
#### Step 2: Clone Repository
```bash
git clone https://git.bugsy.cz/beval/roster.git
cd roster
```
#### Step 3: Build
```bash
meson setup builddir
meson compile -C builddir
```
#### Step 4: Install
```bash
sudo meson install -C builddir
```
#### Step 5: Run
```bash
roster
```
Or launch from your application menu.
### Method 2: Build with Flatpak (Recommended)
Flatpak provides a sandboxed environment with all dependencies included.
#### Step 1: Install Flatpak Builder
**Fedora:**
```bash
sudo dnf install flatpak-builder
```
**Ubuntu/Debian:**
```bash
sudo apt install flatpak-builder
```
#### Step 2: Add Flathub Remote
```bash
flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
```
#### Step 3: Install GNOME SDK
```bash
flatpak install flathub org.gnome.Platform//49 org.gnome.Sdk//49
```
#### Step 4: Build Flatpak
```bash
git clone https://git.bugsy.cz/beval/roster.git
cd roster
flatpak-builder --user --install --force-clean build-dir cz.bugsy.roster.json
```
#### Step 5: Run
```bash
flatpak run cz.bugsy.roster
```
### Method 3: GNOME Builder (For Development)
GNOME Builder provides an integrated development environment.
#### Step 1: Install GNOME Builder
```bash
flatpak install flathub org.gnome.Builder
```
#### Step 2: Open Project
1. Launch GNOME Builder
2. Click "Clone Repository"
3. Enter: `https://git.bugsy.cz/beval/roster.git`
4. Click "Clone"
#### Step 3: Build and Run
1. Click the "Build" button (hammer icon)
2. Click the "Run" button (play icon)
## Uninstallation
### Native Installation
```bash
cd roster/builddir
sudo ninja uninstall
```
### Flatpak Installation
```bash
flatpak uninstall cz.bugsy.roster
```
## File Locations
### Native Installation
- **Binary**: `/usr/local/bin/roster` (or `/usr/bin/roster`)
- **Application data**: `~/.local/share/cz.bugsy.roster/`
- **Requests/Projects**: `~/.local/share/cz.bugsy.roster/requests.json`
- **Session state**: `~/.local/share/cz.bugsy.roster/session.json`
- **Sensitive variables**: GNOME Keyring (encrypted)
### Flatpak Installation
- **Binary**: Managed by Flatpak
- **Application data**: `~/.var/app/cz.bugsy.roster/data/cz.bugsy.roster/`
- **Requests/Projects**: `~/.var/app/cz.bugsy.roster/data/cz.bugsy.roster/requests.json`
- **Session state**: `~/.var/app/cz.bugsy.roster/data/cz.bugsy.roster/session.json`
- **Sensitive variables**: GNOME Keyring (encrypted)
## Troubleshooting
### Build Errors
**Error: "meson: command not found"**
- Install Meson build system (see dependencies above)
**Error: "Package 'gtk4' not found"**
- Install GTK 4 development files (see dependencies above)
**Error: "Package 'libadwaita-1' not found"**
- Install libadwaita development files (see dependencies above)
### Runtime Errors
**Error: "Failed to access Secret Service"**
- Ensure GNOME Keyring is installed and unlocked
- For Flatpak: Check that D-Bus permission is granted
**Error: "Module 'gi' not found"**
- Ensure PyGObject is installed: `sudo dnf install python3-gobject`
### Flatpak Specific
**Error: "error: Nothing matches org.gnome.Platform"**
- Add Flathub remote: `flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo`
- Install GNOME SDK: `flatpak install flathub org.gnome.Platform//49 org.gnome.Sdk//49`
**Application doesn't have network access**
- Check Flatpak permissions: `flatpak info --show-permissions cz.bugsy.roster`
- Should include: `--share=network`
## Next Steps
- [[Getting-Started]] - Learn how to use Roster
- [[Projects-and-Environments]] - Organize your API testing
- [[Variables]] - Use variables in requests

55
Keyboard-Shortcuts.md Normal file

@ -0,0 +1,55 @@
# Keyboard Shortcuts
Complete list of keyboard shortcuts in Roster.
## Request Management
| Action | Shortcut |
|--------|----------|
| New request tab | **Ctrl+T** |
| Close current tab | **Ctrl+W** |
| Send request | **Ctrl+Return** |
| Save request | **Ctrl+S** |
## Navigation
| Action | Shortcut |
|--------|----------|
| Next tab | **Ctrl+Tab** |
| Previous tab | **Ctrl+Shift+Tab** |
| Focus URL field | **Ctrl+L** |
## Application
| Action | Shortcut |
|--------|----------|
| Show keyboard shortcuts | **Ctrl+?** |
| Quit application | **Ctrl+Q** |
## Dialog Management
| Action | Shortcut |
|--------|----------|
| Close dialog | **Escape** |
| Confirm action | **Return** or **Enter** |
## Text Editing
Standard text editing shortcuts work in all text fields:
| Action | Shortcut |
|--------|----------|
| Select all | **Ctrl+A** |
| Copy | **Ctrl+C** |
| Cut | **Ctrl+X** |
| Paste | **Ctrl+V** |
| Undo | **Ctrl+Z** |
| Redo | **Ctrl+Shift+Z** |
## Tips
**Quick Send:** After editing request, press **Ctrl+Return** to send immediately without clicking.
**Fast Tab Switching:** Use **Ctrl+Tab** to cycle through open requests.
**Save Often:** Press **Ctrl+S** frequently to avoid losing work.

@ -0,0 +1,255 @@
# Projects and Environments
Projects help organize related HTTP requests, while environments allow you to manage different configurations (development, staging, production).
## Projects
### What is a Project?
A project is a container for:
- **Saved requests** - Your HTTP requests
- **Variables** - Shared variables across requests
- **Environments** - Different configurations (dev, prod, etc.)
Example projects:
- "GitHub API"
- "My App Backend"
- "Payment Integration"
- "Authentication Service"
### Creating a Project
1. Click the **hamburger menu** (three lines) in top-left
2. Select **"New Project"**
3. Enter project name
4. Click **"Create"**
A default "Default" environment is automatically created.
### Accessing Projects
Projects appear in the sidebar on the left side:
- Click a project to expand/collapse
- Shows saved requests within the project
- Active project highlighted
### Renaming a Project
1. Right-click the project in sidebar
2. Select **"Rename"**
3. Enter new name
4. Click **"Save"**
### Deleting a Project
1. Right-click the project in sidebar
2. Select **"Delete"**
3. Confirm deletion
**Warning:** This deletes all requests, environments, and variables in the project!
### Changing Project Icon
1. Right-click the project in sidebar
2. Select **"Change Icon"**
3. Choose icon from grid
4. Icon updates immediately
## Environments
### What is an Environment?
An environment is a set of variable values. Common environments:
- **Production** - Live API with real credentials
- **Staging** - Pre-production testing
- **Development** - Local development server
- **Testing** - Automated test environment
### Managing Environments
See [[Variables]] for complete environment and variable management documentation.
**Quick access:**
1. Select a project
2. Click **"Environments"** button in header bar
3. Manage environments and variables in dialog
### Default Environment
Each project starts with a "Default" environment. You can:
- Rename it
- Add more environments
- Delete it (if you have at least one other environment)
## Saved Requests
### Saving a Request
1. Configure your request (URL, method, headers, body)
2. Click **"Save"** button in header
3. Enter request name (e.g., "Get User Profile")
4. Click **"Save"**
Request is now saved to the current project.
### Opening a Saved Request
1. Click the hamburger menu
2. Navigate to your project
3. Click on a saved request
4. Request opens in a new tab
### Editing a Saved Request
1. Open the request
2. Make changes
3. Click **"Save"** (or Ctrl+S)
4. Changes are saved
**Modified indicator:** Unsaved changes show a dot (•) on the tab label.
### Deleting a Saved Request
1. Right-click the request in sidebar
2. Select **"Delete"**
3. Confirm deletion
### Attaching Scripts
See [[Scripts]] for complete documentation.
1. Save a request first
2. Right-click the request in sidebar
3. Select **"Edit Scripts"**
4. Write preprocessing/postprocessing scripts
5. Click **"Save"**
## Switching Between Projects
### In Sidebar
Simply click different projects in the sidebar to view their contents.
### Request Association
Each request tab remembers its project:
- Variables come from that project's environments
- Saving updates that project
## Organization Tips
### Group by Service
Create separate projects for each external API:
```
Projects:
GitHub API
- List Repositories
- Create Issue
- Get User
Stripe API
- Create Customer
- Create Payment
- List Charges
```
### Group by Feature
Create projects for features in your app:
```
Projects:
Authentication
- Login
- Logout
- Refresh Token
User Management
- Create User
- Update Profile
- Delete Account
```
### Use Descriptive Names
**Good request names:**
- "Login with Email/Password"
- "Get User Profile by ID"
- "Create Payment Intent (Stripe)"
- "Update User Settings"
**Bad request names:**
- "Request 1"
- "Test"
- "New Request"
- "GET"
## Project Workflow Example
### Step 1: Create Project
Create "My API" project
### Step 2: Add Environments
Open Environments dialog, add:
- Production
- Development
### Step 3: Define Variables
Add variables:
- `base_url`
- Production: `https://api.example.com`
- Development: `http://localhost:3000`
- `api_key` (mark as sensitive)
- Production: `prod-key-***`
- Development: `dev-key-***`
### Step 4: Create Requests
Create and save requests:
- "Get Users" - `GET {{base_url}}/users`
- "Create User" - `POST {{base_url}}/users`
- "Delete User" - `DELETE {{base_url}}/users/{{user_id}}`
### Step 5: Test Different Environments
1. Open "Get Users" request
2. Select "Development" environment
3. Send (hits localhost)
4. Select "Production" environment
5. Send (hits production API)
Same request, different targets!
## Data Storage
### File Location
**Native:**
```
~/.local/share/cz.bugsy.roster/requests.json
```
**Flatpak:**
```
~/.var/app/cz.bugsy.roster/data/cz.bugsy.roster/requests.json
```
### Sensitive Variables
Regular variables stored in JSON.
Sensitive variables stored in GNOME Keyring (encrypted).
See [[Sensitive-Variables]] for details.
### Backup
To backup your projects:
1. Copy the `requests.json` file
2. For sensitive variables: backup your GNOME Keyring (see [[Sensitive-Variables]])
## Next Steps
- [[Variables]] - Learn about variables and environments
- [[Sensitive-Variables]] - Secure credential storage
- [[Scripts]] - Automate workflows

552
Scripts.md Normal file

@ -0,0 +1,552 @@
# Scripts
Roster supports JavaScript preprocessing and postprocessing scripts to automate request modifications and response data extraction.
## Overview
Scripts allow you to:
- **Modify requests** before they're sent (preprocessing)
- **Extract data** from responses after they're received (postprocessing)
- **Chain requests** together by passing data between them
- **Automate workflows** like authentication flows
Scripts are executed using **GJS** (GNOME JavaScript), the same JavaScript runtime used throughout GNOME.
## Script Types
### Preprocessing Scripts
**Run BEFORE the HTTP request is sent.**
Use cases:
- Modify request headers, URL, body, or method
- Add dynamic values (timestamps, request IDs, signatures)
- Read environment variables
- Set/update environment variables
- Add authentication headers
### Postprocessing Scripts
**Run AFTER receiving the HTTP response.**
Use cases:
- Extract data from response body
- Parse JSON/XML responses
- Store values in environment variables for use in subsequent requests
- Validate response data
- Chain requests together
## Adding Scripts to Requests
### Step 1: Save a Request
Scripts are attached to saved requests.
1. Configure your request
2. Click **"Save"** button
3. Enter request name
4. Click **"Save"**
### Step 2: Open Script Editor
1. Right-click the saved request in the sidebar
2. Select **"Edit Scripts"**
3. Script editor dialog opens with two tabs:
- **Preprocessing** tab
- **Postprocessing** tab
### Step 3: Write Your Script
1. Select the appropriate tab
2. Write JavaScript code
3. Click **"Save"** or **Ctrl+S**
4. Close dialog
### Step 4: Run Request with Scripts
1. Open the request
2. Click **"Send"**
3. Preprocessing script runs first (if present)
4. HTTP request sent
5. Postprocessing script runs after response received (if present)
## Preprocessing API
### Available Objects
#### Request Object (Modifiable)
```javascript
request.method // String: "GET", "POST", "PUT", "DELETE"
request.url // String: Full URL
request.headers // Object: Header key-value pairs
request.body // String: Request body
```
All properties can be modified.
#### Roster API
```javascript
// Variables
roster.getVariable(name) // Get variable from selected environment
roster.setVariable(name, value) // Set/update variable
roster.setVariables({key: value}) // Batch set variables
// Project Information
roster.project.name // Current project name
roster.project.environments // Array of environment names
```
#### Console Output
```javascript
console.log(message) // Output shown in preprocessing results tab
console.error(message) // Error output
```
### Examples
#### Example 1: Add Dynamic Authentication Header
```javascript
const token = roster.getVariable('auth_token');
request.headers['Authorization'] = 'Bearer ' + token;
request.headers['X-Request-Time'] = new Date().toISOString();
console.log('Added auth header');
```
#### Example 2: Modify Request Based on Environment
```javascript
const env = roster.getVariable('environment_name');
if (env === 'production') {
request.url = request.url.replace('localhost', 'api.example.com');
console.log('Switched to production URL');
} else {
console.log('Using development URL');
}
```
#### Example 3: Generate Request Signature
```javascript
const apiKey = roster.getVariable('api_key');
const timestamp = Date.now().toString();
const requestId = Math.random().toString(36).substring(7);
request.headers['X-API-Key'] = apiKey;
request.headers['X-Timestamp'] = timestamp;
request.headers['X-Request-ID'] = requestId;
// Save for later reference
roster.setVariable('last_request_id', requestId);
console.log('Request ID:', requestId);
```
#### Example 4: Add HMAC Signature
```javascript
// Note: GJS doesn't have crypto module, this is pseudocode
const apiKey = roster.getVariable('api_key');
const secretKey = roster.getVariable('secret_key');
const timestamp = Date.now().toString();
// You would use a crypto library here
const signature = generateHMAC(request.body + timestamp, secretKey);
request.headers['X-API-Key'] = apiKey;
request.headers['X-Timestamp'] = timestamp;
request.headers['X-Signature'] = signature;
```
#### Example 5: Modify Request Body
```javascript
// Parse existing body
const body = JSON.parse(request.body);
// Add dynamic fields
body.timestamp = new Date().toISOString();
body.requestId = Math.random().toString(36).substring(7);
body.version = '2.0';
// Update request body
request.body = JSON.stringify(body, null, 2);
console.log('Modified request body');
```
## Postprocessing API
### Available Objects
#### Response Object (Read-Only)
```javascript
response.body // String: Response body
response.headers // Object: Header key-value pairs
response.statusCode // Number: HTTP status code (200, 404, etc.)
response.statusText // String: Status text ("OK", "Not Found", etc.)
response.responseTime // Number: Response time in milliseconds
```
All properties are read-only.
#### Roster API
```javascript
// Variables
roster.setVariable(name, value) // Set/update variable
roster.setVariables({key: value}) // Batch set variables
```
Note: `roster.getVariable()` is NOT available in postprocessing.
#### Console Output
```javascript
console.log(message) // Output shown in postprocessing results tab
console.error(message) // Error output
```
### Examples
#### Example 1: Extract Authentication Token
```javascript
const data = JSON.parse(response.body);
if (data.access_token) {
roster.setVariable('auth_token', data.access_token);
console.log('Saved auth token');
} else {
console.error('No access token in response');
}
```
#### Example 2: Extract Multiple Values
```javascript
const data = JSON.parse(response.body);
roster.setVariables({
user_id: data.user.id,
user_name: data.user.name,
user_email: data.user.email,
session_id: data.session.id
});
console.log('Extracted user:', data.user.name);
```
#### Example 3: Validate and Store Response
```javascript
const data = JSON.parse(response.body);
if (response.statusCode === 200 && data.items) {
roster.setVariable('item_count', data.items.length.toString());
if (data.items.length > 0) {
roster.setVariable('first_item_id', data.items[0].id);
roster.setVariable('first_item_name', data.items[0].name);
}
console.log('Found', data.items.length, 'items');
} else {
console.error('Error: Invalid response or no items');
}
```
#### Example 4: Extract Pagination Token
```javascript
const data = JSON.parse(response.body);
if (data.next_page_token) {
roster.setVariable('next_page', data.next_page_token);
console.log('Next page token saved');
} else {
roster.setVariable('next_page', '');
console.log('No more pages');
}
```
#### Example 5: Parse Response Headers
```javascript
// Extract rate limit information from headers
const rateLimit = response.headers['X-RateLimit-Limit'];
const rateRemaining = response.headers['X-RateLimit-Remaining'];
const rateReset = response.headers['X-RateLimit-Reset'];
if (rateLimit && rateRemaining && rateReset) {
roster.setVariables({
rate_limit: rateLimit,
rate_remaining: rateRemaining,
rate_reset: rateReset
});
console.log('Rate limit:', rateRemaining, '/', rateLimit);
} else {
console.log('No rate limit headers');
}
```
## Complete Workflows
### Workflow 1: OAuth Token Flow
**Request 1: Login (POST /auth/login)**
Postprocessing:
```javascript
// Extract and store tokens from login response
const data = JSON.parse(response.body);
if (response.statusCode === 200) {
roster.setVariables({
access_token: data.access_token,
refresh_token: data.refresh_token,
user_id: data.user_id,
expires_at: data.expires_at
});
console.log('Logged in as user:', data.user_id);
} else {
console.error('Login failed:', response.statusText);
}
```
**Request 2: Get User Profile (GET /users/{userId})**
Preprocessing:
```javascript
// Use stored token and user ID in request
const token = roster.getVariable('access_token');
const userId = roster.getVariable('user_id');
// Add authentication
request.headers['Authorization'] = 'Bearer ' + token;
// Substitute user ID in URL
request.url = request.url.replace('{userId}', userId);
console.log('Making authenticated request for user:', userId);
```
### Workflow 2: Paginated API Requests
**Request: Get Items with Pagination**
Preprocessing:
```javascript
const nextPage = roster.getVariable('next_page');
if (nextPage) {
request.url = request.url + '?page_token=' + nextPage;
console.log('Fetching next page');
} else {
console.log('Fetching first page');
}
```
Postprocessing:
```javascript
const data = JSON.parse(response.body);
// Store items count
roster.setVariable('items_count', data.items.length.toString());
// Store next page token for subsequent request
if (data.next_page_token) {
roster.setVariable('next_page', data.next_page_token);
console.log('Page loaded. More pages available.');
} else {
roster.setVariable('next_page', '');
console.log('Page loaded. No more pages.');
}
```
### Workflow 3: Dynamic API Versioning
**Preprocessing:**
```javascript
const apiVersion = roster.getVariable('api_version') || 'v1';
request.url = request.url.replace('/api/', '/api/' + apiVersion + '/');
request.headers['X-API-Version'] = apiVersion;
console.log('Using API version:', apiVersion);
```
**Postprocessing:**
```javascript
// Check if server suggests upgrading API version
const suggestedVersion = response.headers['X-Suggested-API-Version'];
if (suggestedVersion) {
console.log('Server suggests API version:', suggestedVersion);
roster.setVariable('suggested_api_version', suggestedVersion);
}
```
## Script Execution Flow
When you click "Send" on a request with scripts:
```
1. Load request from UI
2. IF preprocessing script exists:
2a. Execute preprocessing script
2b. Script can modify request object
2c. Script output shown in "Preprocessing" tab
3. Apply variable substitution ({{variable_name}})
4. Send HTTP request
5. Receive response
6. IF postprocessing script exists:
6a. Execute postprocessing script
6b. Script can read response, set variables
6c. Script output shown in "Postprocessing" tab
7. Display response in UI
8. Save to history
```
## Error Handling
### Script Errors
If a script throws an error:
- Error message shown in script results tab
- Request still executes (preprocessing) or response still displays (postprocessing)
- Variable changes before the error are preserved
### Example: Safe JSON Parsing
```javascript
try {
const data = JSON.parse(response.body);
roster.setVariable('user_id', data.user.id);
console.log('Success');
} catch (e) {
console.error('Failed to parse JSON:', e.message);
}
```
## Best Practices
### Keep Scripts Simple
Scripts should be short and focused:
- One clear purpose per script
- Avoid complex logic
- Use console.log for debugging
### Error Handling
Always handle potential errors:
```javascript
if (response.statusCode === 200) {
try {
const data = JSON.parse(response.body);
// ... process data
} catch (e) {
console.error('Parse error:', e.message);
}
} else {
console.error('Request failed:', response.statusText);
}
```
### Variable Naming
Use clear, descriptive variable names:
```javascript
// Good
roster.setVariable('github_access_token', token);
roster.setVariable('user_profile_id', id);
// Bad
roster.setVariable('token', token);
roster.setVariable('id', id);
```
### Logging
Use console output to track script execution:
```javascript
console.log('Starting authentication...');
// ... authentication logic
console.log('Authentication complete');
```
### Sensitive Data
**Never log sensitive data:**
```javascript
// BAD - logs the actual token
const token = roster.getVariable('api_key');
console.log('Token:', token);
// GOOD - confirms without revealing
const token = roster.getVariable('api_key');
console.log('Token retrieved');
```
## Limitations
### No External Libraries
GJS environment is sandboxed:
- Cannot import npm packages
- Cannot require() external modules
- Standard JavaScript built-ins available
### No Asynchronous Operations
Scripts execute synchronously:
- No setTimeout/setInterval
- No async/await
- No fetch() or XMLHttpRequest
- Use Roster's request system instead
### Security Restrictions
Scripts cannot:
- Access file system directly
- Make network requests (other than the main request)
- Execute shell commands
- Access system resources
## Debugging Scripts
### Use Console Output
Liberally use console.log:
```javascript
console.log('Request URL before:', request.url);
// ... modify URL
console.log('Request URL after:', request.url);
```
### Check Script Results
After sending request:
1. Look for "Preprocessing" tab (if preprocessing script exists)
2. Look for "Postprocessing" tab (if postprocessing script exists)
3. View console output and any errors
### Test with Simple Scripts
Start simple and build up:
```javascript
// Test 1: Verify script runs
console.log('Script is running!');
// Test 2: Verify variable access
const baseUrl = roster.getVariable('base_url');
console.log('Base URL:', baseUrl);
// Test 3: Verify request modification
request.headers['X-Test'] = 'hello';
console.log('Added test header');
```
## Next Steps
- [[API-Reference]] - Complete API documentation
- [[Variables]] - Learn about environment variables
- [[Sensitive-Variables]] - Secure storage for tokens and keys

377
Sensitive-Variables.md Normal file

@ -0,0 +1,377 @@
# Sensitive Variables
Sensitive variables provide secure storage for API keys, passwords, and tokens using GNOME Keyring encryption instead of plain text files.
## Overview
Regular [[Variables]] are stored in plain text JSON files at `~/.local/share/cz.bugsy.roster/requests.json`. This is fine for non-sensitive data like base URLs or timeout values, but **dangerous for secrets**.
**Sensitive variables** solve this by storing values encrypted in GNOME Keyring, the same secure storage used by:
- Firefox and GNOME Web for passwords
- Evolution for email credentials
- Network Manager for WiFi passwords
- SSH for key passphrases
## How It Works
### Storage Comparison
**Regular Variable (Insecure):**
```
File: ~/.local/share/cz.bugsy.roster/requests.json
Format: Plain text (anyone can read)
Content: "api_key": "secret-abc123-visible-to-all"
```
**Sensitive Variable (Secure):**
```
File: ~/.local/share/cz.bugsy.roster/requests.json
Format: Plain text
Content: "api_key": "" (empty placeholder)
Keyring: GNOME Keyring (encrypted)
Format: Encrypted with your login password
Content: "secret-abc123-visible-to-all" (encrypted at rest)
```
## Usage
### Step 1: Open Environments Dialog
1. Select your project from the sidebar
2. Click the **"Environments"** button in the header bar
3. The variables table appears
### Step 2: Identify the Lock Icon
Each variable row has three buttons:
- Variable name entry
- **Lock icon** (toggle between secure/insecure storage)
- Delete button (trash icon)
### Step 3: Mark Variable as Sensitive
**Before entering secrets (recommended):**
1. Create variable (e.g., `api_key`)
2. Click the **lock icon** (appears as unlocked/gray initially)
3. Icon changes to locked state (with accent color)
4. Variable name becomes bold and colored
5. Now enter your secret value
**After entering secrets (migration):**
1. Click the lock icon on existing variable
2. Values automatically move from JSON to keyring
3. JSON file now contains empty placeholders
### Step 4: Enter Secret Values
1. Click in the value entry field
2. Type your secret value
3. **You'll see bullets (••••••) instead of characters**
4. Value automatically saves to GNOME Keyring (encrypted)
### Step 5: Use in Requests
Sensitive variables work exactly like regular variables:
```
GET {{base_url}}/users
Authorization: Bearer {{api_key}}
```
When you send the request:
1. Roster retrieves `api_key` from GNOME Keyring
2. Decrypts it using your login password
3. Substitutes it into the request
4. Sends the request with the real value
## Visual Indicators
### Lock Icon States
**Unlocked (channel-insecure-symbolic):**
- Gray/inactive appearance
- Variable stored in plain JSON
- Tooltip: "Not sensitive (stored in JSON) - Click to mark as sensitive"
**Locked (channel-secure-symbolic):**
- Colored/active appearance (accent color)
- Variable stored in encrypted keyring
- Tooltip: "Sensitive (stored in keyring) - Click to make non-sensitive"
### Variable Name Styling
**Regular variables:**
- Normal text color
- Normal font weight
**Sensitive variables:**
- Accent color (theme-dependent)
- Bold font weight
- Easy to identify at a glance
### Value Entry Fields
**Regular variables:**
- Show typed characters normally
- Standard text entry
**Sensitive variables:**
- Show bullets (••••••) like password fields
- Entry purpose: PASSWORD
- Cannot see the value while typing
## Viewing Secrets in Keyring
To verify your secrets are encrypted:
1. Open **"Passwords and Keys"** application (Seahorse)
2. Navigate to **"Login"** keyring
3. Look for entries labeled: `Roster: ProjectName/EnvironmentName/VariableName`
Example:
```
Login Keyring
Roster: GitHub API/Production/api_token
Roster: GitHub API/Development/api_token
Roster: Stripe API/Production/secret_key
```
Click any entry to:
- View the secret value (requires authentication)
- Edit the value manually
- Delete the secret
## Making Variables Non-Sensitive
If you marked a variable as sensitive by mistake:
1. Click the lock icon again
2. Icon changes from locked to unlocked
3. Values move from keyring back to JSON
4. You can now see values in plain text
**Warning:** Only do this for non-sensitive data! Once moved to JSON, the values are visible to anyone who can read your files.
## Security
### Encryption
- Values encrypted at rest with your GNOME login password
- Uses same encryption as system passwords
- No plaintext secrets in file system
### Auto-Unlock
- Keyring unlocks automatically when you log in
- No need to enter password again
- Locked when you log out
### Access Control
- Only your user account can access
- Protected by OS-level security
- Sandboxed apps (Flatpak) require explicit permission
### Separation
Roster secrets are stored with a custom schema:
- Schema: `cz.bugsy.roster.EnvironmentVariable`
- Attributes: `project_id`, `environment_id`, `variable_name`
- **Won't clutter your personal passwords**
- Easy to identify in Seahorse
## Best Practices
### DO Mark as Sensitive
- **API keys**: `api_key`, `github_token`, `stripe_key`
- **Passwords**: `password`, `db_password`, `admin_pass`
- **Tokens**: `bearer_token`, `oauth_token`, `access_token`
- **Secrets**: `client_secret`, `api_secret`, `webhook_secret`
- **Private keys**: Any cryptographic keys
### DON'T Mark as Sensitive
- **URLs**: `base_url`, `api_endpoint`, `webhook_url`
- **Configuration**: `timeout`, `max_retries`, `api_version`
- **Identifiers**: `env`, `region`, `datacenter`, `tenant_id`
- **Non-secret data**: Anything that's OK to be visible
### Workflow Tips
**Create variables first, then mark as sensitive:**
```
1. Add variable "api_key"
2. Click lock icon (mark as sensitive)
3. Enter value (goes to keyring immediately)
```
**Use descriptive names:**
```
Good: github_personal_token, stripe_secret_key
Bad: token1, key, secret
```
**Group related variables:**
```
base_url (regular)
api_key (sensitive)
api_secret (sensitive)
timeout (regular)
```
## Automatic Cleanup
When you delete variables, environments, or projects, Roster automatically cleans up keyring secrets:
**Delete variable:**
- Removed from JSON
- All keyring secrets for that variable deleted (across all environments)
**Delete environment:**
- Removed from JSON
- All keyring secrets for that environment deleted
**Delete project:**
- Project removed from disk
- **All keyring secrets for that project deleted**
**Rename variable:**
- Variable renamed in JSON
- All keyring secrets automatically renamed
- No data loss
## Script Integration
Scripts can access sensitive variables transparently:
### Reading Sensitive Variables
```javascript
// Preprocessing script
const apiKey = roster.getVariable('api_key'); // Retrieved from keyring
request.headers['X-API-Key'] = apiKey;
console.log('Using API key'); // DON'T log the actual value!
```
### Writing Sensitive Variables
```javascript
// Postprocessing script
const data = JSON.parse(response.body);
// If 'access_token' is marked as sensitive, it goes to keyring
roster.setVariable('access_token', data.access_token);
console.log('Saved access token to keyring');
```
Scripts don't need to know whether variables are sensitive - storage is handled automatically.
## Troubleshooting
### Keyring Not Unlocked
**Symptom:** Error accessing secrets
**Solution:**
1. Ensure you're logged into GNOME
2. Keyring unlocks automatically on login
3. Manual unlock: Open "Passwords and Keys", right-click "Login", select "Unlock"
### Flatpak Permission Denied
**Symptom:** Cannot access keyring from Flatpak
**Solution:**
1. Check `cz.bugsy.roster.json` includes: `--talk-name=org.freedesktop.secrets`
2. Rebuild Flatpak with correct permissions
3. Verify: `flatpak info --show-permissions cz.bugsy.roster`
### Cannot View Secret in Seahorse
**Symptom:** Secret exists but won't display
**Solution:**
1. Right-click the secret in Seahorse
2. Select "Show password"
3. Enter your login password when prompted
### Lost Secrets After Reinstall
**Symptom:** Secrets missing after reinstalling Roster
**Keyring persists across installs** - secrets should still be there.
**Check:**
1. Open Seahorse
2. Look under "Login" keyring
3. Search for "Roster:"
If missing, you'll need to re-enter them.
## Advanced: Manual Keyring Management
You can manually manage Roster secrets using Seahorse:
### View All Roster Secrets
1. Open Seahorse
2. Navigate to "Login" keyring
3. Search or filter for: `Roster`
4. All Roster secrets appear
### Backup Secrets
Seahorse can export keyring for backup:
1. File → Export
2. Select secrets to export
3. Choose secure location
### Delete All Roster Secrets
1. Search for "Roster" in Seahorse
2. Select all Roster entries
3. Right-click → Delete
## Migration from Plain Variables
If you have existing projects with secrets in plain text:
```javascript
// Step 1: Identify secrets in your variables
// Look for: api_key, password, token, secret, private_key
// Step 2: For each secret variable:
// - Open Environments dialog
// - Click lock icon
// - Values automatically move to keyring
// Step 3: Verify in Seahorse
// - Open "Passwords and Keys"
// - Check secrets appear under "Login" keyring
```
Your JSON file will now have empty strings, and secrets are encrypted.
## Platform Differences
### Native Installation
- Keyring: `~/.local/share/keyrings/`
- Direct access to system keyring
- No special permissions needed
### Flatpak Installation
- Keyring: Same system keyring (shared)
- Requires D-Bus permission: `--talk-name=org.freedesktop.secrets`
- Sandboxed but can access keyring with permission
Both installations share the same keyring - secrets work across install methods!
## Next Steps
- [[Variables]] - Learn about regular variables
- [[Scripts]] - Use variables in automation scripts
- [[API-Reference]] - Complete JavaScript API documentation

356
Variables.md Normal file

@ -0,0 +1,356 @@
# Variables
Variables allow you to reuse values across multiple requests and switch between different environments (development, staging, production) with ease.
## What are Variables?
Variables are placeholder values that you can reference in your requests using the `{{variable_name}}` syntax. When you send a request, Roster automatically replaces these placeholders with the actual values from your selected environment.
**Example:**
```
URL: {{base_url}}/users
Headers:
Authorization: Bearer {{api_token}}
```
When sent with the "Production" environment:
```
URL: https://api.example.com/users
Headers:
Authorization: Bearer prod-token-abc123
```
## Environments
Environments are collections of variable values. Common environment names:
- **Production** - Live API credentials
- **Staging** - Pre-production testing
- **Development** - Local development server
Each project can have multiple environments, and each environment has its own set of variable values.
## Creating Environments
### Step 1: Open Environments Dialog
1. Select a project from the sidebar
2. Click the **"Environments"** button in the header bar
### Step 2: Add Environment
1. Click **"Add Environment"** button
2. Enter name (e.g., "Production", "Development")
3. Click **"Add"**
The environment appears as a new column in the table.
### Step 3: Add Variables
1. Click the **"+"** button at the bottom of the variable list
2. Enter variable name (e.g., `base_url`)
3. Click **"Add"**
The variable appears as a new row in the table.
### Step 4: Set Values
Click in the cells to enter values for each environment:
| Variable | Production | Development |
|----------|-----------|-------------|
| base_url | `https://api.example.com` | `http://localhost:3000` |
| timeout | `30` | `60` |
Click **"Done"** to save.
## Using Variables in Requests
### Variable Syntax
Use double curly braces: `{{variable_name}}`
Variables work in:
- **URL**: `{{base_url}}/users`
- **Headers**: `Authorization: Bearer {{token}}`
- **Request Body**: `{"api_key": "{{api_key}}"}`
### Select Environment
1. Open a request tab
2. Use the **environment dropdown** (top of request panel)
3. Select environment (e.g., "Production")
4. All variables in the request will use values from this environment
### Visual Indicators
**Defined variables** - Normal appearance
**Undefined variables** - Highlighted with a warning color
- Variable name exists in `{{...}}` syntax
- But not defined in current environment
## Managing Variables
### Rename Variable
1. Open **Environments** dialog
2. Click on variable name
3. Edit the name
4. Press Enter
The variable is renamed across all environments automatically.
### Delete Variable
1. Open **Environments** dialog
2. Click the **trash icon** next to variable name
3. Confirm deletion
The variable is removed from all environments.
### Rename Environment
1. Open **Environments** dialog
2. Click the **edit icon** on environment column header
3. Enter new name
4. Click **"Save"**
### Delete Environment
1. Open **Environments** dialog
2. Click the **trash icon** on environment column header
3. Confirm deletion
All variable values for that environment are deleted.
**Note:** Each project must have at least one environment.
## Common Variable Patterns
### Base URL
```
Variable: base_url
Production: https://api.example.com
Development: http://localhost:3000
Usage: {{base_url}}/users/123
```
### API Version
```
Variable: api_version
Production: v2
Development: v2-beta
Usage: {{base_url}}/{{api_version}}/users
```
### Timeout Values
```
Variable: timeout
Production: 30
Development: 60
Usage: X-Timeout: {{timeout}}
```
### Environment Identifier
```
Variable: env
Production: prod
Development: dev
Usage: X-Environment: {{env}}
```
### User ID for Testing
```
Variable: test_user_id
Production: 12345
Development: 1
Usage: {{base_url}}/users/{{test_user_id}}
```
## Script Integration
Variables can be accessed and modified from [[Scripts]]:
### Reading Variables (Preprocessing)
```javascript
const baseUrl = roster.getVariable('base_url');
const apiKey = roster.getVariable('api_key');
console.log('Using base URL:', baseUrl);
request.headers['X-API-Key'] = apiKey;
```
### Writing Variables (Postprocessing)
```javascript
// Extract token from response and save for next request
const data = JSON.parse(response.body);
roster.setVariable('auth_token', data.access_token);
roster.setVariable('user_id', data.user_id);
console.log('Saved token for user:', data.user_id);
```
### Batch Updates
```javascript
// Update multiple variables at once
roster.setVariables({
access_token: data.access_token,
refresh_token: data.refresh_token,
user_id: data.user.id,
user_name: data.user.name
});
```
Scripts automatically update variables in the currently selected environment.
## Variable Scoping
**Project Level:**
- Variables are defined at the project level
- All requests in the project can access these variables
**Environment Level:**
- Variable values are specific to each environment
- Switching environments switches all values at once
**Not Global:**
- Variables from one project cannot be used in another project
- Each project has its own isolated set of variables
## Best Practices
### Use Descriptive Names
Good:
- `base_url`
- `api_key`
- `github_token`
- `stripe_secret_key`
Bad:
- `url1`
- `key`
- `token`
### Group Related Variables
```
# API Configuration
base_url
api_version
timeout
# Authentication
api_key
api_secret
auth_token
# Test Data
test_user_id
test_email
```
### Keep Environments Consistent
All environments should have the same variable names with different values:
| Variable | Production | Development |
|----------|-----------|-------------|
| base_url | `api.example.com` | `localhost:3000` |
| api_key | `prod-key-***` | `dev-key-***` |
| timeout | `30` | `60` |
### Use Sensitive Variables for Secrets
**DO NOT** store secrets in regular variables - they are saved in plain text!
Use [[Sensitive-Variables]] instead:
- API keys
- Passwords
- Tokens
- Secret keys
## Undefined Variables
When Roster encounters `{{variable_name}}` but the variable is not defined:
**Visual Warning:**
- The entry field with undefined variable is highlighted
- Warning color indicates missing variable
**Request Behavior:**
- The literal text `{{variable_name}}` is sent
- No error is thrown
- Check the console for warnings
**Fix:**
1. Open **Environments** dialog
2. Add the missing variable
3. Set its value in current environment
## Environment Switching
Switch environments to test the same request against different APIs:
**Example Workflow:**
1. Create request: `GET {{base_url}}/users`
2. Select "Development" environment
3. Send request (goes to `http://localhost:3000/users`)
4. Select "Production" environment
5. Send request (goes to `https://api.example.com/users`)
Same request, different targets!
## Advanced Topics
### Variables in Request Body
```json
{
"api_key": "{{api_key}}",
"environment": "{{env}}",
"user_id": {{user_id}}
}
```
Note: Numeric values should not have quotes if you want them as numbers.
### Nested Variable References
Variables can contain other variables:
```
Variable: full_url
Value: {{base_url}}/{{api_version}}/users
```
Both `{{base_url}}` and `{{api_version}}` will be substituted.
### Dynamic Variables from Scripts
Variables don't have to be manually created - scripts can create them:
```javascript
// Preprocessing script
const timestamp = new Date().toISOString();
roster.setVariable('request_timestamp', timestamp);
// Now you can use {{request_timestamp}} in the request
```
## Next Steps
- [[Sensitive-Variables]] - Store API keys and secrets securely
- [[Scripts]] - Automate variable management with JavaScript
- [[API-Reference]] - Complete API for working with variables