Frequently Asked Questions
Common questions about TinySystems development and usage.
General
What is TinySystems?
TinySystems is a Kubernetes-native, flow-based application development platform. It allows you to build applications by connecting reusable components in visual flows, with each component running as a Kubernetes resource.
How does TinySystems differ from other workflow tools?
Key differences:
- Kubernetes-native: Components run as Kubernetes resources, leveraging K8s for scaling, networking, and state
- Go-based SDK: Write components in Go with full type safety
- Blocking execution: Synchronous message passing ensures reliable processing
- CR-based state: State propagation via Custom Resources enables horizontal scaling
What can I build with TinySystems?
- API integrations and data pipelines
- Webhook processors and event handlers
- Scheduled automation tasks
- Data transformation workflows
- Multi-service orchestration
- Internal tools and dashboards
Development
How do I create a new component?
- Create a Go struct implementing
module.Component - Implement
GetInfo(),Ports(),Handle(), andInstance() - Register with a module
- Build and deploy
See the Component Interface guide.
Why does my component need to implement Instance()?
Instance() returns a fresh component instance for each TinyNode. This ensures:
- No shared state between nodes
- Clean initialization
- Proper garbage collection
func (c *Component) Instance() module.Component {
return &Component{}
}How do I add configuration to my component?
Create a Settings struct and expose it via the _settings port:
type Settings struct {
Timeout string `json:"timeout" default:"30s"`
}
func (c *Component) Ports() []module.Port {
return []module.Port{
{
Name: v1alpha1.SettingsPort,
Source: true,
Configuration: Settings{},
},
}
}Why isn't my struct tag appearing in the UI?
Check that:
- The field is exported (capitalized)
- The
jsontag is present - The tag syntax is correct (no spaces around
=) - You're using supported tags (
title,description,required, etc.)
How do I debug component execution?
- Use standard Go logging in your component
- Check pod logs:
kubectl logs <pod> - Enable debug mode in settings
- Use the TinySystems dashboard for flow visualization
Execution Model
Why does output() block?
The blocking model provides:
- Backpressure: Slow consumers prevent fast producers from overwhelming the system
- Error propagation: Errors bubble up through the call chain
- Reliable delivery: Messages aren't lost to buffer overflows
- Predictable execution: Easy to reason about flow behavior
Can I send messages without blocking?
Yes, wrap in a goroutine:
go output(ctx, "notification", msg)But be careful:
- You lose error handling
- No backpressure
- The component may complete before the message is delivered
What happens when downstream processing fails?
The error propagates back through the output() call chain:
err := output(ctx, "result", data)
if err != nil {
// Downstream failed - handle the error
return err
}How do I handle errors in my component?
Option 1: Return the error (blocks the upstream)
return fmt.Errorf("processing failed: %w", err)Option 2: Send to error port (continues flow)
output(ctx, "error", ErrorOutput{Error: err.Error()})
return nilKubernetes Integration
Why isn't my TinyNode being processed?
Check:
- Module is deployed and running
- TinyModule CR exists for the module
- Namespace matches
- No errors in controller logs
- Component name matches
How do I expose an HTTP endpoint?
Use the resource manager via _client port:
c.resourceManager.ExposePort(ctx, resource.ExposePortRequest{
Port: 8080,
Hostnames: []string{"api.example.com"},
})This creates Service and Ingress resources automatically.
How does leader election work?
Each TinyNode gets a Kubernetes Lease. The pod that acquires the lease becomes leader:
if utils.IsLeader(ctx) {
// Leader-only operations
}Leaders handle write operations; readers handle read-only tasks.
Can multiple pods handle the same TinyNode?
Yes, with the leader-reader pattern:
- One leader handles state changes
- Multiple readers can process read-only requests
- State propagates via TinyNode metadata
Expressions
Why isn't my expression working?
Common issues:
- Missing
$for root reference:{{$.field}}not{{field}} - Wrong quotes: Use single quotes inside expressions:
{{'text'}} - Missing closing braces:
{{$.field}} - Type mismatch: Ensure numeric operations on numbers
How do I provide a default value?
Use the OR operator:
name: "{{$.user.name || 'Anonymous'}}"
count: "{{$.count || 0}}"Can I use complex logic in expressions?
Expressions support:
- Path access:
{{$.deep.nested.value}} - Operators:
{{$.a + $.b}} - Conditionals:
{{$.active ? 'yes' : 'no'}} - Functions:
{{upper($.name)}}
For complex logic, use a Modify component instead.
How do I access array elements?
first: "{{$.items[0]}}"
last: "{{$.items[-1]}}"
dynamic: "{{$.items[$.index]}}"Scaling
How do I scale my flow?
- Module pods: Scale the Deployment
- Kubernetes resources: Adjust resource requests/limits
- Leader-reader: Implement the pattern for read-heavy workloads
- Parallel processing: Use iterator/aggregator patterns
Why isn't scaling improving performance?
Consider:
- Is the bottleneck CPU/memory or I/O?
- Are you using leader-only operations?
- Is there contention on shared resources?
- Does the flow support parallelization?
How do I share state across pods?
Use TinyNode metadata via _reconcile port:
// Leader writes
output(ctx, v1alpha1.ReconcilePort, func(n *v1alpha1.TinyNode) {
n.Status.Metadata["key"] = "value"
})
// All pods read
value := node.Status.Metadata["key"]Deployment
How do I deploy my module?
- Build Docker image
- Push to registry
- Create Helm chart
- Deploy with Helm
See Building Modules.
What Kubernetes version is required?
TinySystems requires Kubernetes 1.20+ for:
- Custom Resource support
- Server-side apply
- Lease-based leader election
Can I run TinySystems locally?
Yes, using:
- Minikube
- Kind (Kubernetes in Docker)
- k3d
- Docker Desktop Kubernetes
Troubleshooting
Component not receiving messages
- Check edges are correctly configured
- Verify port names match exactly
- Check source/target node names
- Look for errors in controller logs
Messages being lost
- Are you using non-blocking output without error handling?
- Check for panics in component code
- Verify the downstream component is healthy
- Check resource limits (memory, CPU)
Slow performance
- Profile your component code
- Check for N+1 patterns in database queries
- Review blocking operations
- Consider caching frequently accessed data
Pod keeps restarting
- Check memory limits (OOM kills)
- Look for panics in logs
- Verify environment variables are set
- Check liveness/readiness probes
Best Practices
How should I structure my module?
my-module/
├── cmd/
│ └── main.go
├── components/
│ ├── component1/
│ │ └── component.go
│ └── component2/
│ └── component.go
├── chart/
│ └── my-module/
├── go.mod
└── DockerfileWhen should I create a new component vs. use expressions?
Use expressions for:
- Simple data mapping
- Field renaming
- Basic transformations
Create a component for:
- Complex business logic
- External API calls
- State management
- Custom protocols
How do I handle secrets?
- Store in Kubernetes Secrets
- Reference via configRef in settings
- Never log secret values
- Use RBAC to restrict access
apiKey:
configRef:
name: my-secret
key: api-keyGetting Help
Where can I get support?
- Documentation: This site
- GitHub Issues: Report bugs and feature requests
- Community: Join discussions
How do I report a bug?
- Check existing issues
- Create a minimal reproduction
- Include:
- TinySystems version
- Kubernetes version
- Component code (if applicable)
- Error messages and logs
- Steps to reproduce