Managing multiple development processes has always been a pain point for developers. We’ve all been there - juggling terminal windows, tabs, and tmux sessions, trying to keep track of what’s running where. Runy.dev addresses this fundamental problem with an elegant architecture that combines the performance of Rust with the flexibility of TypeScript.

The Problem Space

Before diving into Runy’s architecture, let’s understand the problem it solves:

  1. Terminal Proliferation: Modern development often requires running multiple services simultaneously - frontend dev servers, backend APIs, databases, message queues, and more
  2. Context Switching: Constantly switching between terminal windows breaks flow and reduces productivity
  3. Process Lifecycle Management: Starting, stopping, and restarting services manually is tedious and error-prone
  4. Log Management: Tracking logs from multiple services across different terminals is challenging
  5. Team Onboarding: New team members struggle to understand which services to run and how

Runy’s Architectural Approach

Runy takes a unique approach to solving these problems through a carefully designed architecture that separates concerns while maintaining performance and usability.

Core Architecture Components

The architecture consists of three main layers:

┌─────────────────────────────────────────┐
│         Web Dashboard (Browser)         │
│         http://localhost:4122           │
└────────────────┬────────────────────────┘
                 │ HTTP/WebSocket
┌────────────────▼────────────────────────┐
│          Runy Daemon (Rust)             │
│     - Process Management Engine         │
│     - gRPC Server                       │
│     - Web Server                        │
└────────────────┬────────────────────────┘
                 │ gRPC
┌────────────────▼────────────────────────┐
│        Runy CLI & SDK (TypeScript)      │
│     - Command Line Interface            │
│     - TypeScript Configuration API      │
└─────────────────────────────────────────┘

The Rust Core: Performance Where It Matters

The heart of Runy is written in Rust, and this choice is deliberate. Process management requires:

  • Low-level system access: Spawning processes, managing file descriptors, signal handling
  • Performance: Minimal overhead when managing multiple processes
  • Reliability: The daemon must be stable and handle edge cases gracefully
  • Resource efficiency: Low memory footprint even when managing many processes

Rust provides all of these capabilities while ensuring memory safety and preventing common bugs that plague system-level software.

The daemon architecture follows a client-server model:

  1. Automatic Lifecycle Management: The daemon starts automatically when needed and can run indefinitely
  2. gRPC Communication: Uses Protocol Buffers for efficient, type-safe communication between CLI and daemon
  3. Process Supervision: Monitors child processes, captures output, and handles lifecycle events
  4. Web Server: Serves the dashboard interface and provides real-time updates via WebSockets

TypeScript SDK: Developer-Friendly Configuration

While the core is in Rust, Runy provides a TypeScript SDK for configuration. This design decision offers several advantages:

import { workspace } from "@runy-dev/core";

const ws = workspace(import.meta);

ws.service("api-server", (s) => {
  s.autorun(); // Start automatically when workspace loads

  s.run(async (ctx) => {
    // Complex initialization logic
    await setupDatabase();

    // Start the actual process
    await ctx.process({
      alias: "api",
      cmd: "node",
      args: ["./dist/server.js"],
      env: {
        PORT: "3000",
        NODE_ENV: "development"
      }
    });
  });
});

Benefits of this approach:

  1. Type Safety: Full TypeScript support with autocompletion in IDEs
  2. Familiar Syntax: Developers can use JavaScript/TypeScript they already know
  3. Complex Logic: Support for async operations, conditionals, and programmatic configuration
  4. Composability: Services can depend on and interact with each other

Web Dashboard: Visual Process Management

The web dashboard at http://localhost:4122 provides a unified view of all running processes:

  • Real-time Logs: Stream logs from all services in one place
  • Process Control: Start, stop, restart services with a click
  • Resource Monitoring: CPU and memory usage for each process
  • Workspace Organization: Group related services together
  • Search and Filter: Quickly find specific log entries or services

The dashboard uses WebSockets for real-time updates, ensuring you see log output and status changes immediately.

Workspace Organization

Runy introduces the concept of “workspaces” - logical groupings of related services. This maps naturally to how developers think about their projects:

const ws = workspace(import.meta);

// Frontend services
ws.service("frontend", (s) => {
  s.run(async (ctx) => {
    await ctx.process({
      alias: "next",
      cmd: "npm",
      args: ["run", "dev"],
      cwd: "./frontend"
    });
  });
});

// Backend services
ws.service("backend", (s) => {
  s.run(async (ctx) => {
    await ctx.process({
      alias: "express",
      cmd: "npm",
      args: ["run", "start"],
      cwd: "./backend"
    });
  });
});

// Infrastructure
ws.service("database", (s) => {
  s.run(async (ctx) => {
    await ctx.process({
      alias: "postgres",
      cmd: "docker",
      args: ["run", "postgres:14"]
    });
  });
});

Comparison with Alternatives

vs Terminal Multiplexers (tmux/screen)

While tmux is powerful, it has a steep learning curve and doesn’t provide:

  • Visual process management
  • Programmatic configuration
  • Easy team sharing of configurations
  • Integrated log management

vs Process Managers (PM2, Supervisor)

Traditional process managers are often designed for production use and lack:

  • Developer-focused UI
  • TypeScript configuration
  • Workspace organization
  • Integrated development workflow

vs Docker Compose

Docker Compose is excellent for containerized services but:

  • Requires containerization of all services
  • Adds overhead for local development
  • Less flexible for mixed environments
  • Steeper learning curve for simple use cases

Implementation Details Worth Noting

Process Isolation

Each process runs in its own environment with:

  • Separate stdout/stderr capture
  • Environment variable isolation
  • Working directory management
  • Signal handling for clean shutdowns

Log Management

Runy implements a sophisticated log pipeline:

  1. Capture stdout/stderr from processes
  2. Timestamp and tag each line
  3. Buffer for performance
  4. Stream to connected clients
  5. Optional persistence for debugging

Error Recovery

The architecture includes several resilience patterns:

  • Automatic restart on failure (configurable)
  • Graceful degradation when services fail
  • Health checks for critical services
  • Clean shutdown sequences

Future Architectural Directions

While the current architecture is solid, there are interesting possibilities for evolution:

  1. Plugin System: Allow extending Runy with custom functionality
  2. Distributed Mode: Manage processes across multiple machines
  3. Cloud Integration: Deploy and manage cloud resources
  4. AI Assistant: Natural language process management
  5. Performance Profiling: Built-in profiling for managed processes

Open Source Considerations

Currently, the Rust core remains closed source while the SDK and documentation are open. This hybrid model allows:

  • Rapid iteration on core functionality
  • Community contributions to SDK and integrations
  • Potential future open-sourcing of the core
  • Commercial sustainability for continued development

Conclusion

Runy’s architecture represents a thoughtful approach to developer tooling. By combining Rust’s performance with TypeScript’s developer experience and a modern web interface, it creates a powerful yet approachable solution to process management.

The separation of concerns - with Rust handling system-level operations, TypeScript providing configuration, and the web dashboard offering visualization - allows each component to excel at its specific role. This architecture not only solves immediate developer pain points but also provides a foundation for future enhancements.

Whether you’re managing a complex microservices architecture locally or just tired of juggling terminal windows, Runy’s architecture offers a glimpse into how modern developer tools can simplify our workflows without sacrificing power or flexibility.


Comments