In 0.7.0 we built a plugin sandbox and told you it was safe. In 0.8.0 we built everything plugins actually need to build real applications.

This is the release where GoatFlow stops being a ticketing system with a plugin API and starts being an application platform. Plugins can now declare custom fields, render their own UIs, store encrypted secrets, scope data by organisation, and get listed in a marketplace. And we shipped the whole thing five months ahead of schedule.
┌─────────────────────────────────────────────┐
│ GoatKit PaaS Core │
│ │
│ Plugin │
│ ┌───────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Custom │ │ Plugin │ │ Secure │ │
│ │ Fields │ │ UIs │ │ Settings │ │
│ └─────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ Organisations │ │
│ │ per-org config · query scoping │ │
│ │ org switcher · membership │ │
│ └─────────────────────────────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌───────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Entity │ │ Self- │ │ Market- │ │
│ │ Deletion │ │ Service │ │ place │ │
│ │ GDPR │ │ Auth │ │ gk CLI │ │
│ └───────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────┘
Custom Fields
The headline feature. Plugins declare custom fields on any entity — tickets, contacts, agents, queues, organisations — and the platform handles everything: storage, validation, UI rendering, and querying. No more extension tables.
Fifteen field types ship out of the box, including three GIS types for location-aware applications:
| Type | What it stores |
|---|---|
| text, textarea, integer, decimal, boolean | The basics |
| date, datetime | Temporal data |
| select, multi_select | Controlled vocabularies |
| url, email, phone | Contact information |
| point (lat/lng) | Map coordinates with bounding-box queries |
| polygon (GeoJSON) | Geographic boundaries |
| address | Structured addresses with auto-geocode |
Plugins register fields in their GKRegistration manifest. Names are auto-prefixed to prevent collisions. The platform creates them in the database at load time, validates values server-side (including regex with timeout protection against ReDoS), and renders them automatically on entity pages.
Admins can also create custom fields directly — no plugin required. The admin UI supports the same 15 types.
Legacy OTRS dynamic_field entries migrate automatically on startup. It’s a copy, not a move — downgrade safely if you need to.
Plugin UI System
Plugins can now declare independent UIs: customer-facing apps, agent tools, admin panels, public status pages, kiosks. Each UI gets its own routes, branding, navigation, and optional PWA manifest.
Three shell types control how much GoatFlow chrome appears:
- Standard — full nav bar, theme support, same look as native pages
- Minimal — slim header, optional bottom/top/side navigation, mobile-first
- None — raw HTML, plugin controls the entire page
Routes are namespaced under /ui/{plugin}_{ui_id}/ — no wildcard conflicts with existing endpoints. (We learned that lesson the hard way with a /:entity_type/:id/custom-fields route that ate our ticket API. Lesson noted, pattern banned.)
PWA manifests generate automatically at /ui/{id}/manifest.json. Users can install plugin apps to their phone home screen.
Organisations & Multi-Tenancy
The gk_organisation entity provides hierarchical organisations with user membership (agents AND customers), per-org sysconfig overrides, and automatic HostAPI query scoping.
The key design decision: per-org settings extend the existing sysconfig system rather than introducing a JSON blob. ConfigGet("max_attachments") resolves through a four-tier cascade — user preference, org override, system override, system default — and plugins don’t need to know about any of it.
Query scoping is automatic. When a plugin calls DBQuery("SELECT * FROM ticket WHERE queue_id = ?"), the sandbox transparently appends AND org_id = ? for org-aware tables. Plugins get tenant isolation for free.
Secure Settings
AES-256-GCM encrypted key-value storage via HostAPI. Plugins store API keys, webhook secrets, and device PINs without touching cryptography. The platform manages the encryption key, storage, and masked admin display (last 4 characters only).
Secrets are org-scoped — different API keys for different organisations, with automatic fallback to global values.
Entity Deletion
Two deletion patterns, unified across all entity types:
Soft delete moves entities to a recycle bin, anonymises PII fields with [DELETED] (irreversible — this is GDPR-grade), and notifies plugins via cascade handlers. Configurable retention periods auto-purge expired entries.
Hard delete physically removes entities and all linked data. Requires the entity.hard_delete RBAC permission (admin only). Tombstone logging records that a deletion happened without recording what was deleted.
The recycle bin admin UI lets admins browse deleted items, restore them, or purge permanently. Plugin cascade handlers (CascadeSpec in GKRegistration) get called on both soft and hard delete — so plugin data stays in sync.
Plugin Marketplace
gk install inventory — that’s the workflow. The marketplace uses GitHub Releases as the distribution backend with a curated JSON index. No infrastructure to maintain, zero hosting cost.
The gk CLI gained three commands: install, update, and search. Dependency resolution ensures plugins are loaded in the right order. Circular dependency detection catches problems at install time, not at 3am.
Theme-as-plugin support means community themes ship as plugin ZIPs — install them the same way you install any plugin.
For Kubernetes deployments, GOATFLOW_PLUGIN_ISOLATION=k8s generates Deployment + Service + NetworkPolicy YAML for each gRPC plugin, running them as isolated pods instead of local processes.
Self-Service Authentication
Customer-facing self-service: password recovery (email-based reset tokens with anti-enumeration), customer self-registration with admin approval workflow, email verification, and CAPTCHA integration (reCAPTCHA v3 and hCaptcha).
The registration flow: customer submits form, receives verification email, admin reviews and approves/rejects. No accounts appear until explicitly approved.
Reusable UI Components
Seven server-rendered components usable by any plugin:
| Component | What it does |
|---|---|
gk-daily-queue | Task list with priority indicators and action buttons |
gk-week-calendar | Week grid with colour-coded events |
gk-progress-bar | Animated counter with configurable colour |
gk-stat-card | Dashboard metric with trend indicator |
gk-quick-action | Mobile-friendly tap targets |
gk-file-dropzone | Drag-and-drop upload with progress |
gk-presence-indicator | Real-time “who’s viewing this” via SSE |
All components use CSS variables for theming and have WCAG 2.1 AA accessibility (ARIA attributes, keyboard navigation, screen reader announcements).
Security Fixes
Two vulnerabilities fixed in this release:
Stored XSS in ticket notes (gotrs-io/gotrs-ce#176): HTML content rendered via |safe in templates allowed <script> injection. Fixed with defence-in-depth — server-side bluemonday sanitisation on both write and read paths.
Null byte injection in file uploads: Filenames like shell.php\x00.jpg passed extension validation but the OS truncated at the null byte. Now rejected outright.
Security headers added: CSP with frame-ancestors 'none' (anti-clickjacking), base-uri 'self', form-action 'self', X-Content-Type-Options: nosniff.
By the Numbers
- 9 major feature areas shipped
- 6 database migrations (000009–000014)
- 6 design specifications written
- 15 field types for custom fields
- 7 reusable UI components
- 15 languages with native translations
- 5 months ahead of the original September target
- 2 days of implementation (26–28 March 2026)
What’s Next
0.8.1 (June 2026) brings the Statistics plugin’s Chart.js dashboard widgets, mobile optimisation, WebAuthn/FIDO2 hardware keys, plugin UI offline/service worker support, and performance benchmarks.
0.9.0 (August 2026) ships the first-party open source plugins: FAQ/Knowledge Base, Calendar & Appointments, and Process Management with visual workflow designer.
1.0.0 (November 2026) is the production release: security audit, load testing, comprehensive documentation, and migration tooling from OTRS 6.x.
Bonus Track: What We Learned
The biggest surprise was how much of the work was routing. Not the features — the features were straightforward once designed. But getting routes right took more iteration than any single feature. A wildcard route /:entity_type/:id/custom-fields silently ate our ticket API. A CSP nonce that worked in curl broke every page because Alpine.js needs unsafe-eval internally. The 2FA login path generated JWTs with role=user instead of role=Admin because it was a copy-pasted code path that nobody had tested with admin accounts.
The lesson: design specs save time, tests that verify functionality (not just code paths) catch real bugs, and DRY isn’t just about reducing lines of code — it’s about having one code path for “generate a login token” instead of three that drift apart.
- Source & containers: GoatFlow on GitHub
- Full changelog: CHANGELOG.md
- Helm chart:
oci://ghcr.io/goatkit/charts/goatflow
Questions? Feedback? Open a GitHub Discussion and let us know what you think!