-
v0.5.0 Stable
released this
2026-02-22 21:59:37 +00:00 | 14 commits to main since this releaseVSKI v0.5.0
Features
Complete OpenAPI Documentation for Migrations
-
Added OpenAPI Specs for All Migration Endpoints:
- All migration endpoints now have complete OpenAPI 3.0 specifications
- Properly documented with tags, summaries, operation IDs, and response schemas
- Security requirements (BearerAuth) properly defined for all admin endpoints
- Query parameters, request bodies, and responses fully specified
-
Endpoints Documented:
GET /api/migrations- List all migrations (applied and pending)GET /api/migrations/status- Get the current status of the migration systemGET /api/migrations/generate- Generate a new migration file from schema diffPOST /api/migrations/save- Save a generated migration to a fileGET /api/migrations/load- Load a migration from a filePOST /api/migrations/upload- Upload a migration fileGET /api/migrations/export- Export all migrations as a ZIP filePOST /api/migrations/import- Import migrations from a ZIP fileGET /api/migrations/{version}/download- Download a specific migration file as YAMLPOST /api/migrations/{version}/apply- Apply a migrationPOST /api/migrations/{version}/rollback- Rollback a migrationDELETE /api/migrations/{version}- Delete a migration file and record
New Migrations Management UI
-
Complete Web Interface for Migration Management:
- New Migrations page accessible at
/migrationsin the web dashboard - Full CRUD operations for database migrations
- Visual status indicators (pending, applied, failed)
- New Migrations page accessible at
-
Key Features:
- Migration Status Overview: Shows applied and pending migration counts, last applied version
- Generate Migrations: One-click generation from schema diffs
- Apply/Rollback Migrations: Easy controls to apply or undo migrations
- View Migration Details: View migration YAML files in a modal, with loading feedback
- Download Migrations: Export individual migrations as YAML files
- Export All Migrations: Bundle all migrations as a ZIP file (includes full YAML content)
- Upload Migrations: Import migration files from local system
- Delete Migrations: Remove unwanted migrations with confirmation
-
User Experience:
- Pending migrations shown at the top with prominent Apply buttons
- Applied migrations show rollback options and expandable details view with YAML content
- Real-time status updates after actions
- Loading states for all async operations
- Confirmation dialogs for destructive actions (apply, rollback, delete)
- Error and success notifications for all operations
-
Navigation:
- Migrations link added to SQL section in sidebar navigation
- Accessible via SQL → Migrations in the dashboard menu
Code Refactoring
Unified App Initialization
-
Common App Framework:
- Created
internal/app/bootstrap.gowith shared initialization logic NewApp(cfg *config.Config)function returns configured*gin.Engineand a cleanup function- Eliminated ~90% code duplication between
cmd/serverandcmd/standalone - Both entry points now use the same initialization
- Created
-
NewApp Function Output:
NewAppreturns(*gin.Engine, func(), error)- The
*gin.Engineprovides access to the Gin router for custom routes. - The
func()is a cleanup function to gracefully shut down services (e.g., cron, stats, database connections).
-
Mode-Specific Differences:
- Server Mode (
cmd/server/main.go): UsesNewAppand starts the Gin server. - Standalone Mode (
cmd/standalone/main.go): UsesNewApp, then registers anrouter.NoRoute()handler to serve the embedded web UI. All API routes are available in both modes.
- Server Mode (
Installation
Binary
Download binary for your platform:
# Linux (standalone with embedded UI) wget https://git.vski.sh/x/platform/releases/download/v0.5.0/vski-standalone chmod +x vski-standalone ./vski-standalone # Linux (API-only) wget https://git.vski.sh/x/platform/releases/download/v0.5.0/vski chmod +x vski ./vskiDocker
Pull and run official Docker image:
# Light version (API only, no embedded UI) docker pull git.vski.sh/x/vski:latest # Standalone version (with embedded web UI) docker pull git.vski.sh/x/vski:latest-standalone # Pull specific version docker pull git.vski.sh/x/vski:v0.5.0 docker pull git.vski.sh/x/vski:v0.5.0-standalone # Run light version with default configuration docker run -p 3000:3000 -v $(pwd)/data:/app/data git.vski.sh/x/vski:latest # Run standalone version with embedded web UI docker run -p 3000:3000 -v $(pwd)/data:/app/data git.vski.sh/x/vski:latest-standaloneConfiguration
Create a
.envfile:DATA_DIR=./data SERVER_PORT=3000 JWT_SECRET=your-secret-keyUsage
Accessing OpenAPI Documentation
The complete OpenAPI 3.0 specification is available at:
GET /api/openapi.jsonThis includes all endpoints including the newly documented migration APIs.
Managing Migrations via Web UI
Access the Migrations management page by navigating to:
- URL:
http://localhost:3000/migrations - Navigation: SQL → Migrations in the sidebar
Common Workflows:
-
Generate a new migration:
- Click the "Generate" button to create a migration from current schema diff.
- The new migration will appear in the "Pending Migrations" section and be saved to the
data/migrationsdirectory.
-
Apply a pending migration:
- Find the migration in the "Pending Migrations" list.
- Click the "Apply" button.
- Confirm the action when prompted.
- The migration will move to "Applied Migrations" after successful application.
-
Rollback an applied migration:
- Find the migration in the "Applied Migrations" list.
- Click the "Rollback" button.
- Confirm the rollback action.
- The migration will be reverted to its previous state.
-
View migration details:
- Click the JSON icon (view details) on any migration to display its YAML content in a modal.
- For applied migrations, click the expand chevron (>) to view the YAML content inline.
-
Export migrations:
- Single migration: Click "Download" on a specific migration to save its YAML content.
- All migrations: Click "Export All" to download all migrations as a ZIP file containing individual YAML files.
-
Import migrations:
- Click "Upload" button.
- Select a migration YAML file or a ZIP file containing multiple migration YAMLs.
- Click "Upload" to import the migration(s).
Custom Router Integration
You can now extend the VSKI server with custom routes using the
NewAppfunction:package main import ( "fmt" "git.vski.sh/x/vski/internal/app" "git.vski.sh/x/vski/internal/config" "github.com/gin-gonic/gin" "log" "os" "os/signal" "syscall" ) func main() { cfg := config.Load() router, cleanup, err := app.NewApp(cfg) if err != nil { log.Fatalf("Failed to initialize app: %v", err) } defer cleanup() // Add custom routes router.GET("/custom", func(c *gin.Context) { c.JSON(200, gin.H{"message": "Custom endpoint"}) }) log.Printf("Starting custom server on port %s", cfg.ServerPort) go func() { if err := router.Run(fmt.Sprintf(":%s", cfg.ServerPort)); err != nil { log.Fatalf("Failed to start server: %v", err) } }() quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit log.Println("Shutting down server...") }Migration Notes
Database Migration Required
Adding
fieldRulesColumn tocollectionsTableFor existing deployments, you need to manually add the
fieldRulescolumn to thecollectionstable. Run the following SQL in your database:ALTER TABLE collections ADD COLUMN fieldRules TEXT;Note: This change is backward compatible. Existing collections will have
fieldRulesset toNULL, which the application treats as "no field rules defined" (default open behavior).Adding
fieldEditRulesColumn tocollectionsTableFor existing deployments, you need to manually add the
fieldEditRulescolumn to thecollectionstable. Run the following SQL in your database:ALTER TABLE collections ADD COLUMN fieldEditRules TEXT;Note: This change is backward compatible. Existing collections will have
fieldEditRulesset toNULL, which the application treats as "no field edit rules defined" (default open behavior).Alternative: Automatic Schema Update
If you prefer automatic schema updates, you can restart the VSKI server which will apply any missing columns during database initialization. The
fieldRulescolumn will be automatically added on first startup.Breaking Changes
None.
API Changes
New OpenAPI Endpoints Documented
The following migration endpoints now have complete OpenAPI specifications:
GET /api/migrations- List all migrations (applied and pending)GET /api/migrations/status- Get migration system statusGET /api/migrations/generate- Generate new migration from schema diffPOST /api/migrations/save- Save migration to file (admin utility)GET /api/migrations/load- Load migration from file path (admin utility)POST /api/migrations/upload- Upload migration YAML fileGET /api/migrations/export- Export all migrations as ZIPPOST /api/migrations/import- Import migrations from ZIP fileGET /api/migrations/{version}/download- Download migration YAMLPOST /api/migrations/{version}/apply- Apply migrationPOST /api/migrations/{version}/rollback- Rollback migrationDELETE /api/migrations/{version}- Delete migration
Migration File Format Changes
Collection Changes Model (
internal/models/migrations.go):- Added
drop_columns: []stringfield to track removed columns - Supports soft delete - columns removed from metadata but kept in database
Schema Diff Service (
internal/services/schema_diff.go):detectDropColumns()function identifies removed fieldsisEmptyCollectionChanges()filters unchanged collections- Empty collection changes are excluded from generated migrations
Migration Service (
internal/services/migrations.go):applyCollectionChanges()now handlesdrop_columnsvia metadata update only- Better error handling for rollback data parsing
Code Structure Changes
Before (separate main.go files):
cmd/server/main.go # ~220 lines cmd/standalone/main.go # ~205 linesAfter (shared initialization):
internal/app/bootstrap.go # New file with common logic cmd/server/main.go # ~35 lines (uses app.NewApp(cfg)) cmd/standalone/main.go # ~40 lines (uses app.NewApp(cfg) + standalone handler)Testing
Run E2E Tests
cd vski make build make e2e TEST=50_migrations_test.ts # Test migrations make e2e # Run all E2E testsVerify OpenAPI Spec
# Build the server make build # Start the server ./bin/vski-prod # Fetch OpenAPI spec curl http://localhost:3000/api/openapi.json | jq '.paths | keys | .[] | select(contains("migrations"))'Expected output (example, actual list may vary slightly depending on other APIs):
"/api/migrations" "/api/migrations/status" "/api/migrations/generate" "/api/migrations/save" "/api/migrations/load" "/api/migrations/upload" "/api/migrations/export" "/api/migrations/import" "/api/migrations/{version}/download" "/api/migrations/{version}/apply" "/api/migrations/{version}/rollback" "/api/migrations/{version}"Test Custom Router Integration
package main import ( "fmt" "git.vski.sh/x/vski/internal/app" "git.vski.sh/x/vski/internal/config" "github.com/gin-gonic/gin" "log" "os" "os/signal" "syscall" "net/http" // Added for example ) func main() { cfg := config.Load() router, cleanup, err := app.NewApp(cfg) if err != nil { log.Fatalf("Failed to initialize app: %v", err) } defer cleanup() // Add custom routes router.GET("/custom", func(c *gin.Context) { c.JSON(200, gin.H{"message": "Custom endpoint"}) }) serverPort := cfg.ServerPort // Get the configured port log.Printf("Starting custom server on port %s", serverPort) go func() { if err := router.Run(fmt.Sprintf(":%s", serverPort)); err != nil { log.Fatalf("Failed to start server: %v", err) } }() // Example of calling a custom route time.Sleep(2 * time.Second) // Give server time to start resp, err := http.Get(fmt.Sprintf("http://localhost:%s/custom", serverPort)) if err != nil { log.Printf("Error calling custom route: %v", err) } else { defer resp.Body.Close() if resp.StatusCode == http.StatusOK { log.Println("Custom route works!") } else { log.Printf("Custom route returned status: %d", resp.StatusCode) } } quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit log.Println("Shutting down server...") }Run this code:
go run your_custom_main.goExpected output will show "Custom route works!" if successful.
Documentation
Full documentation available at: https://git.vski.sh/x/platform
Changelog
v0.5.0 (2026-02-22)
Added
- Complete OpenAPI 3.0 specifications for all implemented migration endpoints.
internal/app/bootstrap.gowithNewApp(cfg *config.Config)for unified app initialization.- Full UI for migration CRUD operations (generate, apply, rollback, view, download, upload, export, delete) in
web/src/pages/Migrations.tsx. - Migration status overview, pending/applied migration counts, and last applied version in UI.
- Inline display of migration YAML content for applied migrations via expandable details.
- Loading feedback for asynchronous UI operations in migration management.
- Dropped column tracking in migrations (
drop_columnsfield in collection changes). - Soft delete for columns - dropped columns are untracked from metadata but remain in database (SQLite limitation workaround).
- Filtering of empty collection changes - generated migrations no longer include empty collection entries.
- Better error handling when parsing rollback data and generating migrations.
- Field-level view rules (
fieldRules) - Control READ visibility of individual fields by user groups. - Field-level edit rules (
fieldEditRules) - Control CREATE/UPDATE access to individual fields by user groups. - Field-level permissions override global
viewRule/createRule/updateRulewhen defined. - Split field permissions UI - Separate "View Rules" and "Edit Rules" sections in collection settings with clear visual indicators.
- Required field warnings - UI shows warnings when required fields are not in a group's edit allowed list.
Changed
- Refactored
cmd/server/main.goandcmd/standalone/main.goto useapp.NewApp, significantly reducing code duplication. - Moved
RecoveryMiddlewaretointernal/middleware/middleware.gofor shared use. DownloadMigrationsZipininternal/services/migrations.gonow correctly includes full YAML content of migration files in the ZIP archive and uses proper version matching.- Corrected display logic for "View" migration YAML in web UI modal.
- Removed duplicated UI buttons in
web/src/pages/Migrations.tsx.
Technical
- Implemented backend migration APIs for
list,status,generate,save,load,upload,export,import,download,apply,rollback,delete(global operations, not collection-specific). - App initialization is centralized in
internal/app/bootstrap.go. NewAppreturns(*gin.Engine, func(), error)allowing flexible router extension and proper resource cleanup.- Standalone mode (
cmd/standalone/main.go) integrates embedded web UI viarouter.NoRoute(). - Server mode (
cmd/server/main.go) serves to API without embedded UI. - Frontend
MigrationsPageusessdk.settings.migrationsAPI for all operations with improved user feedback. - Added
DropColumnsfield toCollectionChangesstruct ininternal/models/migrations.go. - Added
detectDropColumns()function ininternal/services/schema_diff.goto identify removed fields. - Added
isEmptyCollectionChanges()helper function to filter out unchanged collections. - Updated
applyCollectionChanges()ininternal/services/migrations.goto handle dropped columns via soft delete. - Improved
GenerateMigrationWithVersion()error handling for rollback data parsing.
Downloads
-
Source code (ZIP)
2 downloads
-
Source code (TAR.GZ)
2 downloads
-
-
v0.4.0 Stable
released this
2026-02-21 17:12:57 +00:00 | 15 commits to main since this releaseVSKI v0.4.0
Bug Fixes
Cross-Database Relations Expand
- Backend Expand Operation Fixed:
- The expand operation now correctly handles relations created via the frontend dashboard
- Backend previously only checked for
collectionIdin relation field options - Now falls back to
toCollectionwhich is used by the frontend when creating collections - Fixes silent failures where expand would return just the ID instead of expanded objects
Frontend Relation Field Improvements
-
Relation Field Selector Display:
- Relation fields in the add/edit record modal now display as a small table
- Shows the first non-system field value (excluding
id,created,updated) - Displays last 4 characters of the record ID for easy identification
- No longer relies on
defaultSearchFieldwhich may not be set
-
Edit Record Relation Fields:
- Fixed crash when editing records with expanded relation fields
- Relation fields are now properly normalized when loading records for editing
- Expanded objects are converted to IDs
- Array relations extract IDs from each expanded item
- Resolves "Objects are not valid as a React child" error
Installation
Binary
Download binary for your platform:
# Linux (standalone with embedded UI) wget https://git.vski.sh/x/platform/releases/download/v0.4.0/vski-standalone chmod +x vski-standalone ./vski-standalone # Linux (API-only) wget https://git.vski.sh/x/platform/releases/download/v0.4.0/vski chmod +x vski ./vskiDocker
Pull and run official Docker image:
# Light version (API only, no embedded UI) docker pull git.vski.sh/x/vski:latest # Standalone version (with embedded web UI) docker pull git.vski.sh/x/vski:latest-standalone # Pull specific version docker pull git.vski.sh/x/vski:v0.4.0 docker pull git.vski.sh/x/vski:v0.4.0-standalone # Run light version with default configuration docker run -p 3000:3000 -v $(pwd)/data:/app/data git.vski.sh/x/vski:latest # Run standalone version with embedded web UI docker run -p 3000:3000 -v $(pwd)/data:/app/data git.vski.sh/x/vski:latest-standaloneConfiguration
Create a
.envfile:DATA_DIR=./data SERVER_PORT=3000 JWT_SECRET=your-secret-keyUsage
Expanding Cross-Database Relations
// Query records with expanded relations (now works correctly) const response = await fetch('http://localhost:3000/api/collections/posts/records?expand=author,comments', { headers: { 'Authorization': 'Bearer YOUR_JWT_TOKEN' } }); const data = await response.json(); console.log(data.items[0].expand.author); // Expanded object, not just IDCreating Collections with Relations (Frontend)
// Frontend creates collections with toCollection option const collection = { name: 'posts', schema: [ { name: 'author', type: 'relation', options: { toCollection: 'users', // Now correctly handled by backend maxSelect: 1 } } ] }; await fetch('http://localhost:3000/api/collections', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_JWT_TOKEN' }, body: JSON.stringify(collection) });Building from Source
# Clone repository git clone https://git.vski.sh/x/platform.git cd platform/vski # Build production binary make build # Build release binary (with UPX compression) make build-release # Run binary ./bin/vskiMigration Notes
No Migration Required
All changes are backward compatible. Existing collections and records will work without any changes.
Breaking Changes
None.
API Changes
Expanded Records Response
The
expandparameter now correctly returns expanded objects for cross-database relations:Before (buggy):
{ "id": "post1", "author": "user123", // Just the ID "expand": { "author": "user123" // Still just the ID, not expanded } }After (fixed):
{ "id": "post1", "author": "user123", "expand": { "author": { "id": "user123", "email": "user@example.com", "name": "John Doe", "created": "2026-02-21T12:00:00Z", "updated": "2026-02-21T12:00:00Z" } } }Testing
Run E2E Tests
cd vski make build make e2e TEST=44_crossdb_expand_test.ts # Test cross-database expand make e2e # Run all E2E testsTest Relation Expand Manually
# Create two collections with relation curl -X POST http://localhost:3000/api/collections \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -d '{ "name": "users", "schema": [ {"name": "id", "type": "text", "system": true, "primary": true}, {"name": "name", "type": "text"} ] }' curl -X POST http://localhost:3000/api/collections \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -d '{ "name": "posts", "schema": [ {"name": "id", "type": "text", "system": true, "primary": true}, {"name": "title", "type": "text"}, {"name": "author", "type": "relation", "options": {"toCollection": "users", "maxSelect": 1}} ] }' # Create records curl -X POST http://localhost:3000/api/collections/users/records \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -d '{"id": "user1", "name": "John"}' curl -X POST http://localhost:3000/api/collections/posts/records \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -d '{"id": "post1", "title": "Hello", "author": "user1"}' # Test expand (should return expanded user object) curl 'http://localhost:3000/api/collections/posts/records?expand=author' \ -H "Authorization: Bearer YOUR_TOKEN"Documentation
Full documentation available at: https://git.vski.sh/x/platform
Changelog
v0.4.0 (2026-02-21)
Fixed
- Backend expand operation now correctly handles
toCollectionoption in relation fields - Fixed silent failures where expand would return just ID instead of expanded objects
- Relation field selector in frontend now displays as small table with field value and ID
- Fixed crash when editing records with expanded relation fields
- Relation fields are now properly normalized (object to ID) when loading for editing
- Resolved "Objects are not valid as a React child" error in record edit modal
Changed
- Frontend relation picker no longer requires
defaultSearchFieldto be set - Frontend automatically detects first non-system field for display
- Last 4 characters of ID shown instead of full ID for better readability
Technical
- Modified
internal/repositories/record_repository.go:634to check bothcollectionIdandtoCollection - Updated
web/components/ui/record-form.tsxto normalize relation fields on record load - Enhanced relation field display logic to work with any expanded object structure
Downloads
-
Source code (ZIP)
2 downloads
-
Source code (TAR.GZ)
1 download
- Backend Expand Operation Fixed:
-
v0.3.0 Stable
released this
2026-02-21 13:34:29 +00:00 | 17 commits to main since this releaseVSKI v0.3.0
Features
OpenAPI Specification Support
-
OpenAPI 3.0.3 Specification:
- Complete OpenAPI spec generation for all API endpoints
- Served via
/openapi.jsonand/openapi.html
-
Interactive API Documentation:
- Embedded Redoc UI with
index.html - Beautiful, responsive API documentation with Try It Now functionality
- JWT Bearer authentication support documented
- Custom theme matching VSKI branding
- Embedded Redoc UI with
-
API Endpoint:
GET /openapi.json- Retrieve OpenAPI 3.0.3 specification- Can be imported into API clients (Postman, Insomnia, etc.)
- Used for auto-generating SDKs and documentation
Enhanced Database Management
-
Database Creation API:
POST /api/databases- Create new database files dynamically- Supports custom descriptions for user databases
- Automatic registration in
_databasestable - Proper error handling and validation
-
Database Listing Improvements:
GET /api/databases- List all databases with metadata- Proper NULL handling for descriptions
- Alphabetical sorting for better UX
- Detailed error logging with structured logs (slog)
-
Database Management Service:
- Physical database file creation in
DATA_DIR - Automatic cleanup on creation failure
- Integration with existing database provider
- Physical database file creation in
-
Reserved Database Protection:
defaultandstatsdatabases are protected from deletion- Cannot create databases with reserved names
- Clear error messages for reserved name violations
Collection Creation Restrictions
-
Stats Database Protection:
- Prevent collection creation in
statsdatabase statsis reserved for system metrics and analytics- Returns clear error message when attempting to create collections
- Prevent collection creation in
-
Enhanced Validation:
- Collection creation service now validates database name
- Prevents accidental data in system databases
Build Process Improvements
-
Simplified Default Build:
make buildnow produces production binary without UPX compression- Faster build times for development iterations
- Smaller binary footprint for production deployments
-
New Build Targets:
make build-release- Production build with UPX compression for smallest sizemake build-prod- Production build without compression- Clear separation between development and release builds
Installation
Binary
Download binary for your platform:
# Linux (standalone with embedded UI) wget https://git.vski.sh/x/platform/releases/download/v0.3.0/vski-standalone chmod +x vski-standalone ./vski-standalone # Linux (API-only) wget https://git.vski.sh/x/platform/releases/download/v0.3.0/vski chmod +x vski ./vskiDocker
Pull and run official Docker image:
# Light version (API only, no embedded UI) docker pull git.vski.sh/x/vski:latest # Standalone version (with embedded web UI) docker pull git.vski.sh/x/vski:latest-standalone # Pull specific version docker pull git.vski.sh/x/vski:v0.3.0 docker pull git.vski.sh/x/vski:v0.3.0-standalone # Run light version with default configuration docker run -p 3000:3000 -v $(pwd)/data:/app/data git.vski.sh/x/vski:latest # Run standalone version with embedded web UI docker run -p 3000:3000 -v $(pwd)/data:/app/data git.vski.sh/x/vski:latest-standaloneConfiguration
Create a
.envfile:DATA_DIR=./data SERVER_PORT=3000 JWT_SECRET=your-secret-keyUsage
Accessing OpenAPI Specification
# Get OpenAPI JSON spec curl http://localhost:3000/openapi.json > openapi.json # View interactive documentation open http://localhost:3000/Creating Databases via API
// Create a new database const response = await fetch('http://localhost:3000/api/databases', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_JWT_TOKEN' }, body: JSON.stringify({ name: 'analytics', description: 'User analytics database' }) }); const database = await response.json(); console.log(database); // { id: 'analytics', name: 'analytics', description: 'User analytics database' }Listing Databases
// List all databases const response = await fetch('http://localhost:3000/api/databases', { method: 'GET', headers: { 'Authorization': 'Bearer YOUR_JWT_TOKEN' } }); const databases = await response.json(); console.log(databases); // [ // { id: 'default', name: 'default', description: 'Main database', created: '...', updated: '...' }, // { id: 'stats', name: 'stats', description: 'Statistics database', created: '...', updated: '...' }, // { id: 'analytics', name: 'analytics', description: 'User analytics database', created: '...', updated: '...' } // ]Error Handling
// Attempt to create reserved database name try { const response = await fetch('http://localhost:3000/api/databases', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_JWT_TOKEN' }, body: JSON.stringify({ name: 'default' // Reserved name! }) }); if (!response.ok) { const error = await response.json(); console.error(error); // { error: 'Cannot create reserved database names (default, stats)' } } } catch (error) { console.error('Failed to create database:', error); } // Attempt to create collection in stats database try { const response = await fetch('http://localhost:3000/api/collections', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_JWT_TOKEN', 'x-dbname': 'stats' // Reserved database! }, body: JSON.stringify({ name: 'my_collection', schema: {} }) }); if (!response.ok) { const error = await response.json(); console.error(error); // { error: 'cannot create collection in reserved database "stats"' } } } catch (error) { console.error('Failed to create collection:', error); }Building from Source
# Clone repository git clone https://git.vski.sh/x/platform.git cd platform/vski # Build production binary (no compression) make build # Build release binary (with UPX compression) make build-release # Run binary ./bin/vskiMigration Notes
Database Migration
No manual migration required. The
_databasestable is automatically initialized with default entries on server startup.Breaking Changes
Database Creation Restrictions:
- Cannot create databases named
defaultorstats(reserved for system use) - Cannot create collections in
statsdatabase - These restrictions were previously unenforced but always intended
Build Process Changes:
make buildno longer uses UPX compression by default- Use
make build-releasefor compressed (smallest size) binaries - This change speeds up development builds while maintaining optimization for releases
Configuration Updates
No configuration changes required. All existing configurations work without modification.
API Endpoints
OpenAPI Specification
Method Endpoint Description GET /openapi.jsonRetrieve OpenAPI 3.0.3 spec Database Management
Method Endpoint Request Body Description GET /api/databases- List all databases POST /api/databases{name, description?}Create new database DELETE /api/databases/:name- Delete database Database Creation Response
{ "id": "analytics", "name": "analytics", "description": "User analytics database" }Database List Response
[ { "id": "default", "name": "default", "description": "Main database", "created": "2026-02-21T12:00:00Z", "updated": "2026-02-21T12:00:00Z" }, { "id": "stats", "name": "stats", "description": "Statistics database", "created": "2026-02-21T12:00:00Z", "updated": "2026-02-21T12:00:00Z" } ]Testing
Run E2E Tests
cd vski make build make e2e TEST=19_webhooks_test.ts # Test webhook triggers make e2e TEST=10_cron_test.ts # Test cron integrationTest OpenAPI Spec
# Start server ./bin/vski # Get and validate OpenAPI spec curl http://localhost:3000/openapi.json | jq . # Validate spec with OpenAPI validator # (requires npx or global install) npx @apidevtools/swagger-cli validate http://localhost:3000/openapi.jsonTest Database Management
# Create database curl -X POST http://localhost:3000/api/databases \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -d '{"name":"testdb","description":"Test database"}' # List databases curl http://localhost:3000/api/databases \ -H "Authorization: Bearer YOUR_TOKEN" # Try to create reserved database (should fail) curl -X POST http://localhost:3000/api/databases \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -d '{"name":"default"}'Documentation
Full documentation available at: https://git.vski.sh/x/platform
Interactive API documentation:
- Local:
http://localhost:3000/(when running standalone binary) - OpenAPI Spec:
http://localhost:3000/openapi.json
Changelog
v0.3.0 (2026-02-21)
Added
- OpenAPI 3.0.3 specification generation for all API endpoints
/openapi.jsonendpoint for retrieving machine-readable API spec- Embedded Redoc UI (
index.html) with interactive documentation - Database creation API (
POST /api/databases) - Database listing with metadata and descriptions
- Database deletion API with reserved name protection
- Reserved database protection (
default,statsfrom deletion) - Collection creation restriction in
statsdatabase build-releasemake target for UPX-compressed production buildslogs/directory to.gitignore
Changed
- Default
make buildno longer applies UPX compression - Database list endpoint now sorts results alphabetically
- Database creation now validates against reserved names
- Collection creation service validates database name before creation
- Database list now uses proper NULL handling for descriptions
Fixed
- Database creation now creates physical database files
- Database listing returns proper error messages on failure
- Clear error messages for reserved database violations
- Proper error logging with structured logs (slog)
Technical
- Added
github.com/getkin/kin-openapi v0.133.0for OpenAPI spec generation - Updated related dependencies for OpenAPI support
- Enhanced error handling throughout database management APIs
- Improved documentation in AGENTS.md
Downloads
-
Source code (ZIP)
1 download
-
Source code (TAR.GZ)
1 download
-
-
v0.2.0 Stable
released this
2026-02-17 18:26:44 +00:00 | 18 commits to main since this releaseVSKI v0.2.0
Features
Enhanced Webhook System
-
Retry Configuration: Configure retry limits (1-100 attempts, default 5) for all webhooks
- Set retry limit per webhook for fine-grained control
- Configurable via collection triggers, standalone webhooks, and cron jobs
- Limits prevent runaway retry loops
-
Exponential Backoff Retry Mechanism:
- Retry delay increases exponentially: 2^0=2s, 2^1=4s, 2^2=8s, 2^3=16s, 2^4=32s
- Prevents hammering downstream services during outages
- Reduces network load and server stress
-
JWT Authentication for Webhooks:
- Optional JWT bearer tokens for webhook authentication
- Tokens include
roles: ["service", "webhook"]for easy server-side verification - Enable per-webhook for secure integrations
-
Restart-Proof Webhook Execution:
- Webhook state persisted in
_webhook_logstable - Automatic retry continuation after server restart
- No lost webhook deliveries during deployments
- Webhook state persisted in
Standalone Webhook Management
-
CRUD API for Standalone Webhooks:
- Create, read, update, delete standalone webhooks
- Manage webhooks independent of collections
- Useful for external integrations and scheduled tasks
-
Webhook Manager Service:
- Dedicated service for webhook lifecycle management
- Centralized webhook execution logic
- Consistent behavior across all webhook sources
-
Webhook Registry in Database:
- New
webhookstable stores webhook configurations - Fields: name, url, method, headers, body, retryLimit, jwtEnabled, active
- Persistent storage survives server restarts
- New
Cron Job Integration
-
Unified Webhook Execution:
- Cron HTTP callbacks now use webhook service
- Same retry logic and logging as collection webhooks
- Consistent experience across all webhook types
-
Enhanced Cron Configuration:
- Configure retry limit for HTTP cron jobs
- Enable JWT auth for authenticated HTTP callbacks
- Custom headers for request authentication
- Request body for POST/PUT/PATCH methods
-
One-Time Execution:
- New
onceflag for cron jobs - When
once: true, job is automatically deleted after successful execution - Perfect for scheduled one-time tasks or delayed execution
- Works with both SQL and HTTP cron jobs
- New
-
Cron Job Updates:
- New
PATCH /api/cron/:nameendpoint for updating existing cron jobs - Update schedule, command, and
onceflag without recreating the job - Cron scheduler automatically reloads updated job configuration
- Frontend edit modal reuses the create form with pre-populated data
- New
Docker Enhancements
-
Standalone Docker Image:
- New Docker image with embedded web UI (
vski:standalone) - Multi-stage build process using
denoland/deno:2.6.6official image - Web frontend built and embedded during Docker build
- No need to build web frontend separately
- New Docker image with embedded web UI (
-
Docker Build Automation:
- New
docker-build-standalonemake target for building standalone image - New
docker-push-standalonemake target for pushing standalone image - Automatic copying of web source from
../web/before build - Versioned and latest tags automatically applied
- New
-
Release Process Integration:
- Both light and standalone images built and pushed during release
- Consistent tagging across image variants (
{VERSION},latest) - Automated via
release.tsscript
Frontend Enhancements
-
Updated Cron Settings UI:
- Retry limit configuration (1-100, default 5)
- JWT auth toggle for HTTP jobs
- Custom headers input (JSON format)
- Request body configuration
- "Delete after successful execution" (once) checkbox
-
Webhooks Manager Page (
/settings/webhooks):- List all standalone webhooks
- Create/edit/delete webhooks
- Manual webhook trigger for testing
- Status indicators (active/inactive)
Client SDK Updates
-
Enhanced Trigger Config Types:
retryLimit: number (optional)jwtEnabled: boolean (optional)method: string (optional)headers: Record<string, string> (optional)
-
New Webhook Types:
Webhook: Standalone webhook configurationCreateWebhookRequest: Input for creating webhooksUpdateWebhookRequest: Input for updating webhooks
-
Webhooks Namespace:
sdk.webhooks.list(): List all webhookssdk.webhooks.get(id): Get webhook by IDsdk.webhooks.create(data): Create new webhooksdk.webhooks.update(id, data): Update webhook
-sdk.webhooks.delete(id)`: Delete webhooksdk.webhooks.trigger(id): Manually trigger webhook
Backend API Endpoints
-
Standalone Webhook Management:
GET /api/webhooks- List all webhooksGET /api/webhooks/:id- Get webhook detailsPOST /api/webhooks- Create webhookPUT/PATCH /api/webhooks/:id- Update webhookDELETE /api/webhooks/:id- Delete webhookPOST /api/webhooks/:id/trigger- Manually trigger webhook
-
Cron Job Management:
GET /api/cron- List all cron jobsPOST /api/cron- Create new cron jobPATCH /api/cron/:name- Update existing cron jobDELETE /api/cron/:name- Delete cron job
-
JWT Token Generation:
- New
GenerateTokenWithRoles(id, roles, tokenKey)method - Supports custom role claims (e.g.,
["service", "webhook"]) - Used by webhook service for authenticated requests
- New
Database Schema Changes
-
New
webhooksTable:CREATE TABLE webhooks ( id TEXT PRIMARY KEY, name TEXT UNIQUE NOT NULL, url TEXT NOT NULL, method TEXT DEFAULT 'POST', headers TEXT, body TEXT, retryLimit INTEGER DEFAULT 5, jwtEnabled BOOLEAN DEFAULT 0, active BOOLEAN DEFAULT 1, created TEXT DEFAULT (datetime('now')), updated TEXT DEFAULT (datetime('now')) ); -
Updated
cron_jobsTable:ALTER TABLE cron_jobs ADD COLUMN once BOOLEAN DEFAULT FALSE;- Added
oncecolumn for one-time execution flag - Default value is
false(recurring jobs)
- Added
Installation
Binary
Download binary for your platform:
# Linux (standalone with embedded UI) wget https://git.vski.sh/x/platform/releases/download/v0.2.0/vski-standalone chmod +x vski-standalone ./vski-standalone # Linux (API-only) wget https://git.vski.sh/x/platform/releases/download/v0.2.0/vski chmod +x vski ./vskiDocker
Pull and run official Docker image:
# Light version (API only, no embedded UI) docker pull git.vski.sh/x/vski:latest # Standalone version (with embedded web UI) docker pull git.vski.sh/x/vski:latest-standalone # Pull specific version docker pull git.vski.sh/x/vski:v0.2.0 docker pull git.vski.sh/x/vski:v0.2.0-standalone # Run light version with default configuration docker run -p 3000:3000 -v $(pwd)/data:/app/data git.vski.sh/x/vski:latest # Run standalone version with embedded web UI docker run -p 3000:3000 -v $(pwd)/data:/app/data git.vski.sh/x/vski:latest-standalone # Run with custom environment variables docker run -p 3000:3000 \ -v $(pwd)/data:/app/data \ -e SERVER_PORT=3000 \ -e DATA_DIR=/app/data \ git.vski.sh/x/vski:latest-standaloneConfiguration
Create a
.envfile:DATA_DIR=./data SERVER_PORT=3000 JWT_SECRET=your-secret-keyUsage
Configuring Collection Triggers with Enhanced Options
await client.settings.collections.update(collectionId, { options: JSON.stringify({ triggers: [ { action: "create", url: "https://your-endpoint.com/webhook", method: "POST", headers: { "Authorization": "Bearer token", "X-Custom-Header": "value" }, retryLimit: 5, // Maximum retry attempts jwtEnabled: true // Include JWT bearer token } ] }) });Creating Standalone Webhooks
// Create a new webhook const webhook = await client.webhooks.create({ name: "notification-service", url: "https://api.example.com/notify", method: "POST", headers: JSON.stringify({ "Authorization": "Bearer your-token", "X-Source": "rocketbase" }), body: JSON.stringify({ "message": "Hello from webhook" }), retryLimit: 3, jwtEnabled: true, active: true }); // List all webhooks const webhooks = await client.webhooks.list(); // Update a webhook await client.webhooks.update(webhook.id, { retryLimit: 10, active: false }); // Delete a webhook await client.webhooks.delete(webhook.id); // Manually trigger a webhook for testing await client.webhooks.trigger(webhook.id);Configuring Cron Jobs with Webhook Options
// Create HTTP cron job with retry and JWT await client.cron.create({ name: "daily_report", schedule: "0 9 * * *", // 9 AM daily type: "http", url: "https://api.example.com/reports/daily", method: "POST", body: '{"report_type": "daily"}', headers: JSON.stringify({ "X-Report-Auth": "secret-key" }), retryLimit: 5, jwtEnabled: true }); // Create one-time cron job (deletes after successful execution) await client.cron.create({ name: "send_welcome_email", schedule: "*/5 * * * *", // Run in 5 minutes type: "http", url: "https://api.example.com/send-email", method: "POST", body: JSON.stringify({ "to": "user@example.com", "template": "welcome" }), once: true // Delete after execution }); // Update an existing cron job await client.cron.update("daily_report", { schedule: "0 8 * * *", // Change schedule to 8 AM retryLimit: 10, // Increase retry limit jwtEnabled: true // Enable JWT authentication });Webhook Retry Behavior
Webhooks now retry with exponential backoff:
Attempt Delay Description 1 0s Initial attempt 2 2s First retry 3 4s Second retry 4 8s Third retry 5 16s Fourth retry 6 32s Fifth retry (if limit 5) After exceeding
retryLimit, webhook status is set tofailed.JWT Token Format
When
jwtEnabled: true, webhooks include a JWT bearer token:Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5c2VzdmUzIiwid2ViZCIsInR5c2VyX3dlYmhvb2siLCJleHAiOjE3OTY1MzY5MywiaWF0IjoxNzA4NDM2NjkzfQ.abc123...Token claims:
{ "id": "webhook-service", "roles": ["service", "webhook"], "tokenKey": "webhook-token", "exp": 1739843693 }Querying Webhook Logs
// Filter by collection and status const logs = await client.webhookLogs.list({ collection: "posts", status: "success" }); // Logs include retry information const log = logs[0]; console.log(`Tries: ${log.tries} / ${log.retryLimit || 5}`); console.log(`Status: ${log.status}`);Migration Notes
Database Migration
The
webhookstable is automatically created on server startup. No manual migration required.Breaking Changes
None. This release is fully backward compatible.
Configuration Updates
Existing webhook configurations will continue to work with default values:
retryLimit: Defaults to 5 if not specifiedjwtEnabled: Defaults to false if not specifiedmethod: Defaults to POST if not specified
API Endpoints
Standalone Webhooks
Method Endpoint Description GET /api/webhooksList all webhooks GET /api/webhooks/:idGet webhook by ID POST /api/webhooksCreate new webhook PUT /api/webhooks/:idUpdate webhook PATCH /api/webhooks/:idPartially update webhook DELETE /api/webhooks/:idDelete webhook POST /api/webhooks/:id/triggerManually trigger webhook Webhook Logs
Method Endpoint Query Params Description GET /api/webhooks/logscollection, status, page, perPage Query webhook delivery logs Testing
Run E2E Tests
cd vski make build make e2e TEST=19_webhooks_test.ts # Test webhook triggers make e2e TEST=10_cron_test.ts # Test cron integrationDocumentation
Full documentation available at: https://git.vski.sh/x/platform
Changelog
v0.2.0 (2026-02-17)
Added
- Retry limit configuration (1-100, default 5)
- Exponential backoff retry mechanism (2^n seconds)
- JWT authentication for webhooks with
service,webhookroles - Standalone webhook CRUD API and management UI
- Cron HTTP job integration with webhook service
- Webhook Manager Service for unified webhook handling
- Webhooks client namespace for SDK
- One-time execution flag (
once) for cron jobs - Automatic deletion of cron jobs with
once: trueafter successful execution - Cron job update API (
PATCH /api/cron/:name) for modifying existing jobs - Cron job edit UI in frontend with pre-populated form data
- Docker standalone image with embedded web UI (
vski:standalone) - Multi-stage Docker build process for standalone version
- Docker build automation targets (
docker-build-standalone,docker-push-standalone)
Changed
- Cron HTTP jobs now use webhook service instead of direct HTTP calls
- Enhanced retry logic from fixed 5 attempts to configurable limit
- Retry delay increased from 5s to 2s initial with exponential growth
- Docker build process now supports both light and standalone variants
Fixed
- Webhook state now persists across server restarts
- Consistent logging for all webhook types (triggers, cron, standalone)
Downloads
-
Source code (ZIP)
2 downloads
-
Source code (TAR.GZ)
2 downloads
-
-
v0.1.0 Stable
released this
2026-02-16 21:50:35 +00:00 | 20 commits to main since this releaseVSKI v0.1.0
Features
-
Webhook Triggers: Configure automatic HTTP callbacks on record operations (create, update, delete)
- Define webhooks per collection via collection options
- Support for multiple webhooks per action
- Custom HTTP methods (GET, POST, PUT, DELETE, PATCH)
- Custom headers for authentication and metadata
-
Webhook Execution & Logging:
- Asynchronous webhook execution to avoid blocking operations
- Comprehensive logging of all webhook attempts
- Automatic retry mechanism (max 3 attempts with 5-second delay)
- Detailed status tracking (pending, success, retrying, failed)
-
Webhook Logs API:
- Query webhook logs by collection and status
- View full request/response details
- Track retry attempts and error messages
- Pagination support for large log sets
-
Webhook Payload Structure:
- Includes complete record data in webhook payload
- Metadata: database name, collection name, record ID, action type, timestamp
- JSON-formatted for easy parsing
Installation
Binary
Download binary for your platform:
# Linux (standalone with embedded UI) wget https://git.vski.sh/x/platform/releases/download/v0.1.0/vski-standalone chmod +x vski-standalone ./vski-standalone # Linux (API-only) wget https://git.vski.sh/x/platform/releases/download/v0.1.0/vski chmod +x vski ./vskiDocker
Pull and run official Docker image:
# Pull latest version docker pull git.vski.sh/x/vski:latest # Pull specific version docker pull git.vski.sh/x/vski:v0.1.0 # Run with default configuration docker run -p 3000:3000 -v $(pwd)/data:/app/data git.vski.sh/x/vski:latest # Run with custom environment variables docker run -p 3000:3000 \ -v $(pwd)/data:/app/data \ -e SERVER_PORT=3000 \ -e DATA_DIR=/app/data \ git.vski.sh/x/vski:latestConfiguration
Create a
.envfile:DATA_DIR=./data SERVER_PORT=3000 JWT_SECRET=your-secret-keyUsage
Configuring Webhooks
Webhooks are configured via collection options:
await client.settings.collections.update(collectionId, { options: JSON.stringify({ triggers: [ { action: "create", url: "https://your-endpoint.com/webhook", method: "POST", headers: { "Authorization": "Bearer your-token", "Content-Type": "application/json" } }, { action: "update", url: "https://your-endpoint.com/webhook", method: "PATCH" }, { action: "delete", url: "https://your-endpoint.com/webhook", method: "DELETE" } ] }) });Webhook Payload Example
When a webhook is triggered, you'll receive a JSON payload:
{ "database": "default", "collection": "posts", "id": "123e4567-e89b-12d3-a456-426614174000", "action": "create", "timestamp": "2024-01-15T10:30:00Z", "record": { "id": "123e4567-e89b-12d3-a456-426614174000", "title": "Hello World", "content": "My first post", "created": "2024-01-15T10:30:00Z", "updated": "2024-01-15T10:30:00Z" } }Viewing Webhook Logs
Query webhook logs via API:
const logs = await fetch('/api/webhooks/logs?collection=posts&status=success'); console.log(logs);API Endpoints
GET /api/webhooks/logs
Query webhook logs with optional filters:
Query Parameters:
collection- Filter by collection namestatus- Filter by status (pending, success, retrying, failed)page- Page number (default: 1)perPage- Items per page (default: 30, max: 100)
Response:
[ { "id": "webhook-log-id", "url": "https://your-endpoint.com/webhook", "payload": { ... }, "status": "success", "http_status": 200, "response_body": "...", "error_msg": null, "tries": 1, "created": "2024-01-15T10:30:00Z", "updated": "2024-01-15T10:30:05Z" } ]Migration Notes
No database migration required. The
_webhook_logstable is automatically created on first use.Documentation
Full documentation available at: https://git.vski.sh/x/platform
Downloads
-
Source code (ZIP)
2 downloads
-
Source code (TAR.GZ)
2 downloads
-
-
v0.0.0 Stable
released this
2026-02-15 13:05:20 +00:00 | 24 commits to main since this releaseVSKI v0.0.0
Features
- New standalone binary with embedded web frontend
- Multi-tenant SQLite support
- Real-time WebSocket gateway
- Distributed workflow orchestration
- Vector similarity search with sqlite-vec
- Full-text search with FTS5
- Rule-based security engine
- JWT and API key authentication
- Official Docker image with multi-stage builds
Installation
Binary
Download the binary for your platform:
# Linux (standalone with embedded UI) wget https://git.vski.sh/x/platform/releases/download/v0.0.0/vski-standalone chmod +x vski-standalone ./vski-standalone # Linux (API-only) wget https://git.vski.sh/x/platform/releases/download/v0.0.0/vski chmod +x vski ./vskiDocker
Pull and run the official Docker image:
# Pull latest version docker pull git.vski.sh/x/vski:latest # Pull specific version docker pull git.vski.sh/x/vski:v0.0.0 # Run with default configuration docker run -p 3000:3000 -v $(pwd)/data:/app/data git.vski.sh/x/vski:latest # Run with custom environment variables docker run -p 3000:3000 \ -v $(pwd)/data:/app/data \ -e SERVER_PORT=3000 \ -e DATA_DIR=/app/data \ git.vski.sh/x/vski:latestConfiguration
Create a
.envfile:DATA_DIR=./data SERVER_PORT=3000 JWT_SECRET=your-secret-keyDocumentation
Full documentation available at: https://git.vski.sh/x/platform
Downloads
-
Source code (ZIP)
4 downloads
-
Source code (TAR.GZ)
4 downloads