phs 0.0.7

Runtime for Phlow Script, PHS
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
<p align="center">
  <img src="./docs/phlow.svg" alt="Phlow logo" width="160"/>
  <h1 align="center">Phlow</h1>
</p>

<h2 align="center">Modular Flow Runtime for Composable Backends</h1>

**Phlow** is a **high-performance, scalable, and Low Code flow runtime** built with Rust β€” designed to revolutionize the way you build backends. With Phlow, you can **create APIs, automations, and event-driven systems using just YAML**, composing logic like building blocks.

Thanks to its modular architecture and clear separation between control and behavior, Phlow lets you **orchestrate complex flows without writing code** β€” and when you need more power, just plug in lightweight scripts or Rust modules.

It also comes with **native observability powered by OpenTelemetry**, giving you full visibility into your flows, modules, and executions. Easily export traces and metrics to **Jaeger**, **Grafana Tempo**, or **AWS X-Ray**, all with simple environment variables.

If you're looking for speed, flexibility, and full insight into your backend β€” **Phlow is the Low Code revolution you’ve been waiting for**.

---

## πŸ“š Table of Contents

- [🎯 Philosophy]#-philosophy
- [🧱 Example: `main.yaml` for an HTTP Gateway]#-example-mainyaml-for-an-http-gateway
- [πŸ§ͺ More Examples]#-more-examples
- [πŸ“¦ Packages And Modules]#-packages-and-modules
- [⚑ YAML Superpowers]#-yaml-superpowers
- [βš™οΈ Install & Uninstall]#%EF%B8%8F-installation--uninstall
- [πŸš€ Running a Flow]#-running-a-flow
- [🌐 Running Remote Projects]#-running-remote-projects
- [πŸ”Œ Module Types]#-module-types
- [🧠 Creating Your Own Module: `log`]#-creating-your-own-module-log
- [πŸ“¦ Project Structure]#-project-structure
- [πŸ“‘ Observability]#-observability
- [πŸ§ͺ OpenTelemetry + Jaeger (Local Dev Setup)]#-opentelemetry--jaeger-local-dev-setup
- [🌍 Environment Settings]#-environment-settings
- [πŸ“œ License]#-license

---

## 🎯 Philosophy

### 🧱 1. Radical Modularity  
**Principle:** *Each piece must be independent, reusable, and pluggable.*

Phlow is designed as a set of decoupled modules. You connect functionalities like LEGO blocks, allowing you to replace or evolve parts without breaking the whole. This promotes maintainability and system flexibility.

---

### 🧩 2. Code-Free Composition (Low Code)  
**Principle:** *The flow matters more than the language.*

Business logic is declared using simple files like YAML. Instead of programming behavior, you **compose** it. This empowers both developers and analysts to build together, democratizing software creation.

---

### βš™οΈ 3. High-Performance Runtime  
**Principle:** *Performance is not a detail β€” it's architecture.*

Phlow is built in **Rust**, ensuring memory safety, low resource consumption, and blazing speed. It runs anywhere β€” locally, on the edge, or in the cloud β€” with minimal latency and maximum scalability.

---

### πŸ“¦ 4. Automatic Module Installation  
**Principle:** *The user experience should be instant.*

Phlow detects the required modules and automatically downloads them from the official `phlow-packages` repository. Everything is installed locally under `./phlow-packages`, with no manual setup or external dependencies.

---

### πŸ” 5. Observability by Design  
**Principle:** *You can only improve what you can observe.*

Every flow and module is traceable with **logs, metrics, and spans** via OpenTelemetry. Real-time tracking with Jaeger, Grafana, or Prometheus is built-in. Transparency and traceability are part of the system’s DNA.


---

## 🧱 Example: `main.yaml` for an HTTP Gateway

```yaml
main: gateway

modules:
    - name: gateway
      module: rest_api
      version: latest
      with:
          host: 0.0.0.0
          port: 3000

    - name: request
      version: latest
      module: http_request
      with:
          timeout: 29000 # 29s

steps:
    - condition:
      assert: !eval main.path.start_with("/public")
      then:
        module: request
        input:
            method: !eval main.method
            url: !eval `public-service.local${main.uri}?` 
            headers:
                x-forwarded-for: !eval main.client_ip
                x-original-path: !eval main.path   
            body: !eval main.body
    - use: authorization
      id: auth
      input:
        api_key: !eval main.header.authorization
    - condition:
      assert: !eval steps.auth.authorized == true          
      then:
          module: request
          with:
              method: !eval main.method
              url: !eval `private-service.local${main.uri}?` 
              headers:
                  x-forwarded-for: !eval main.client_ip
                  x-original-path: !eval main.path   
              body: !eval main.body
    - return:
        status_code: 401
        body: {
            "message": "unauthorized",
            "code": 401
        }
```
---

## πŸ§ͺ More Examples

To explore additional use cases and see Phlow in action, check out the [`examples/`](./examples) folder at the root of this repository.

You'll find ready-to-run flows for:

- HTTP gateways
- Task automation
- External API integration
- Using `phs` and `rhai` scripts
- Full observability with spans and logs

Clone, run, and experiment β€” Phlow is made to get you flowing in minutes. πŸš€

---

## πŸ“¦ Packages and Modules

### Automatic Module Download

Phlow automatically downloads the modules specified in your flow configuration.

The official module repository is [phlow-packages](https://github.com/lowcarboncode/phlow-packages), which contains all official Phlow modules precompiled for Linux.

When you run Phlow, it will automatically fetch and install the required modules into a local `phlow-packages/` folder at the root of your project execution.

You don’t need to worry about building or installing them manually β€” just describe the modules in your YAML, and Phlow takes care of the rest.

### Using modules

To use a module in your flow, you only need to declare it under the `modules` section and reference it in your `steps`.

Here’s a minimal working example that uses the official `log` module:

```yaml
main: log_example

modules:
  - module: log
    version: latest

steps:
  - module: log
    input:
      level: info
      message: "πŸ“₯ Starting process..."

  - module: log
    input:
      level: debug
      message: !eval "'Current time: ' + timestamp()"

  - module: log
    input:
      level: error
      message: "❌ Something went wrong"
```

## ⚑ YAML Superpowers

Phlow extends YAML with:

- `!eval`: execute inline expressions using Phlow Script (phs).
- `!include`: include other YAML files into the flow tree.
- `!import`: import external script files (.phs or .rhai) and evaluate them with `!eval`.

---

## βš™οΈ Installation & Uninstall

You can easily install or uninstall Phlow using our ready-to-use shell scripts.

### πŸ”½ Install via `curl`

```bash
curl -fsSL https://raw.githubusercontent.com/lowcarboncode/phlow/main/scripts/install-phlow.sh | bash
```

### πŸ”½ Install via `wget`

```bash
wget -qO- https://raw.githubusercontent.com/lowcarboncode/phlow/main/scripts/install-phlow.sh | bash
```
---

### 🧹 Uninstall via `curl`

```bash
curl -fsSL https://raw.githubusercontent.com/lowcarboncode/phlow/main/scripts/uninstall-phlow.sh | bash
```

### 🧹 Uninstall via `wget`

```bash
wget -qO- https://raw.githubusercontent.com/lowcarboncode/phlow/main/scripts/uninstall-phlow.sh | bash
```
---

These scripts will install or remove the `phlow` binary from `/usr/local/bin`. The install script fetches the latest release and makes it globally available on your system.

### πŸš€ Running a Flow

By default, Phlow will look for a \`main.yaml\` in the current directory:

```bash
phlow
```

To run a specific file:

```bash
phlow path/to/your-flow.yaml
```

If you provide a directory path and it contains a \`main.yaml\`, Phlow will automatically run that:

```bash
phlow path/to/directory
# β†’ runs path/to/directory/main.yaml
```

### πŸ†˜ Help

For all available options and usage info:

```bash
phlow -h
# or
phlow --help
```
---
## 🌐 Running Remote Projects

Phlow supports running remote projects directly from URLs or Git repositories. You can pass a `.git`, `.zip`, or `.tar.gz` source β€” Phlow will automatically download, extract (if needed), and execute the flow from a `main.yaml`.

```bash
# Git via SSH
phlow git@github.com:lowcarboncode/phlow-mirror-request.git 

# Git via HTTPS
phlow https://github.com/lowcarboncode/phlow-mirror-request.git

# ZIP archive
phlow https://github.com/lowcarboncode/phlow-mirror-request/archive/refs/heads/main.zip

# Tarball (GZIP)
phlow https://github.com/lowcarboncode/phlow-mirror-request/tarball/main
```
### πŸͺ„ Git branch selector

```bash
phlow git@github.com:lowcarboncode/phlow-mirror-request.git#develop
```

### πŸ” Custom SSH Key
By default, Phlow uses the SSH key at ~/.ssh/id_rsa to authenticate Git over SSH.
To override this path, set the environment variable:

```bash
export PHLOW_REMOTE_ID_RSA_PATH=/path/to/your/private_key
```

### πŸ” Authorization Header for ZIP/GZIP Downloads
When downloading `.zip` or `.tar.gz` files that require authentication (e.g., from a private server), you can use the environment variable below to send an `Authorization` header in the request:

```bash
export PHLOW_REMOTE_HEADER_AUTHORIZATION="Bearer your_token_here"
```

Phlow will include this header when performing the HTTP request for ZIP or GZIP downloads.


### πŸ—‚οΈ Inner directory selector (ZIP/GZIP)
If you are downloading a ZIP or GZIP archive and want to specify which folder inside the archive contains your flow, you can add `#folder_name` at the end:

```bash
phlow https://github.com/lowcarboncode/phlow-mirror-request/archive/refs/heads/main.zip#phlow-mirror-request
```

### πŸ“ Auto-detection of inner folder
If you don’t specify a folder name and the ZIP/GZIP file contains only one directory, Phlow will automatically treat it as the root and search for a `main.yaml` inside it.

If the archive contains multiple folders or any loose files in the root and no folder is specified, Phlow will return an error.

---

## πŸ”Œ Module Types

| Type         | Purpose                                 |
|--------------|------------------------------------------|
| `main module`| Entry point. Starts the app (HTTP, CLI, AMQP, etc). |
| `step module`| Logic executed within a flow (log, fetch, transform, etc). |

Step modules can also be executed directly from Phlow Script (PHS), making it easy to use simple modules inside .phs or .rhai files.

### πŸ“„ Example: Step Module with Phlow Script (PHS)
#### main.yaml
```yaml
main: cli
name: Example Cli
version: 1.0.0
description: Example CLI module
author: Your Name
modules:
  - module: cli
    version: latest
    with:
      additional_args: false
      args:
        - name: name
          description: Name of the user
          index: 1
          type: string
          required: false
  - module: log
    version: latest
steps:
  - return: !import script.phs
```

#### script.phs
```rust
log("warn", `Hello, ${main.name}`);
"phs"
```

To execute this file, just run:
```bash
2025-04-23T05:23:25.474573Z  WARN log: Hello, Phlow!
phs
```

This will evaluate the imported .phs file and run the steps using the declared modules.

> ℹ️ **Note:** In Phlow Script (PHS), function calls respect the **order of parameters** defined in the module's package. For example, if your `phlow.yaml` for the `log` module defines inputs like:
>
> ```yaml
> input: 
>   type: object
>   required: true
>   properties:
>     level:
>       type: string
>       description: The log level (e.g., info, debug, warn, error).
>       default: info
>       required: false
>     message:
>       type: string
>       description: The message to log.
>       required: true
> ```
>
> Then the correct function signature in `.phs` is:
>
> ```phs
> log(level, message)
> ```
>
> because the parameter order defined in `properties` is preserved and required by the execution engine.


---

## 🧠 Creating Your Own Module: `log`

Phlow modules are written in Rust and compiled as shared libraries. Here’s a real example of a simple **log module** that prints messages at various log levels.

### πŸ”§ Code (`src/lib.rs`)

```rust
use phlow_sdk::tracing_subscriber::prelude::__tracing_subscriber_SubscriberExt;
use phlow_sdk::tracing_subscriber::util::SubscriberInitExt;
use phlow_sdk::tracing_subscriber::Layer;
use phlow_sdk::{
    otel::get_log_level,
    prelude::*,
    tracing_core::LevelFilter,
    tracing_subscriber::{fmt, Registry},
};

create_step!(log(rx));

#[derive(Debug)]
enum LogLevel {
    Info,
    Debug,
    Warn,
    Error,
}

#[derive(Debug)]
struct Log {
    level: LogLevel,
    message: String,
}

impl From<&Value> for Log {
    fn from(value: &Value) -> Self {
        let level = match value.get("level") {
            Some(level) => match level.to_string().as_str() {
                "info" => LogLevel::Info,
                "debug" => LogLevel::Debug,
                "warn" => LogLevel::Warn,
                "error" => LogLevel::Error,
                _ => LogLevel::Info,
            },
            _ => LogLevel::Info,
        };

        let message = value.get("message").unwrap_or(&Value::Null).to_string();

        Self { level, message }
    }
}

pub async fn log(rx: ModuleReceiver) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    Registry::default()
        .with(fmt::layer().with_filter(LevelFilter::from_level(get_log_level())))
        .init();

    debug!("PHLOW_OTEL is set to false, using default subscriber");

    listen!(rx, move |package: ModulePackage| async {
        let value = package.context.input.unwrap_or(Value::Null);
        let log = Log::from(&value);

        match log.level {
            LogLevel::Info => info!("{}", log.message),
            LogLevel::Debug => debug!("{}", log.message),
            LogLevel::Warn => warn!("{}", log.message),
            LogLevel::Error => error!("{}", log.message),
        }

        sender_safe!(package.sender, Value::Null.into());
    });

    Ok(())
}
```
---

### πŸ› οΈ Example usage in a flow

```yaml
steps:
  - module: log
    input:
      level: info
      message: "Process started"

  - use: log
    input:
      level: error
      message: !eval "something went wrong: " + main.error
```
---

## πŸ“¦ Project Structure

```bash
you_project/
β”œβ”€β”€ main.yaml
β”œβ”€β”€ modules.yaml
β”œβ”€β”€ assets/
β”‚   └── body.yaml
β”œβ”€β”€ scripts/
β”‚   └── resolve_url.phs
β”œβ”€β”€ phlow_packages/
β”‚   β”œβ”€β”€ restapi/
β”‚   β”‚   └── module.so
β”‚   β”œβ”€β”€ request/
β”‚   β”‚   └── module.so
β”‚   └── log/
β”‚       └── module.so
```
All compiled `.so` modules **must be placed inside the `phlow_packages/` directory**.

To build all modules at once, this project includes a utility script:
---

## πŸ“‘ Observability

Phlow integrates with:

- OpenTelemetry (OTLP)
- Tracing (Spans and Logs)
- Prometheus Metrics
- Jaeger, Grafana Tempo, AWS X-Ray

Enable it with:

```env
PHLOW_OTEL=true
PHLOW_LOG=DEBUG
PHLOW_SPAN=INFO
```
---

## πŸ§ͺ OpenTelemetry + Jaeger (Local Dev Setup)

To enable observability with **Jaeger** during development, you can run a full OpenTelemetry-compatible collector locally in seconds.

### πŸ”„ 1. Run Jaeger with OTLP support

```bash
docker run -d \
  -p4318:4318 \  # OTLP HTTP
  -p4317:4317 \  # OTLP gRPC
  -p16686:16686 \  # Jaeger UI
  jaegertracing/all-in-one:latest
```
This container supports OTLP over HTTP and gRPC, which are both compatible with Phlow's OpenTelemetry output.

---

### βš™οΈ 2. Configure environment variables

Set the following environment variables in your shell or `.env` file:

```bash
export OTEL_RESOURCE_ATTRIBUTES="service.name=phlow-dev,service.version=0.1.0"
export OTEL_SERVICE_NAME="phlow-dev"
```
---

### πŸ” 3. Open the Jaeger UI

Once running, access the Jaeger web interface at:

[http://localhost:16686](http://localhost:16686)

Search for your service using the name defined in `OTEL_SERVICE_NAME`.

---

### βœ… Tips

- Combine this with `PHLOW_OTEL=true`, `PHLOW_SPAN=INFO`, and `PHLOW_LOG=DEBUG` for full observability.
- You can also integrate with **Grafana Tempo** or **AWS X-Ray** by replacing the collector backend.


---
## 🌍 Environment Settings

Below is a list of **all** environment variables used by the application, combining those defined in both files, along with their descriptions, default values, and types.

### Environment Variables Table

| Variable                                        | Description                                                                                                                       | Default Value | Type    |
|-------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|----------------|---------|
| **PHLOW_PACKAGE_CONSUMERS_COUNT**               | **Number of package consumers**<br>Defines how many threads will be used to process packages.                                     | `10`           | `i32`   |
| **PHLOW_MIN_ALLOCATED_MEMORY_MB**               | **Minimum allocated memory (MB)**<br>Defines the minimum amount of memory, in MB, allocated to the process.                       | `10`           | `usize` |
| **PHLOW_GARBAGE_COLLECTION_ENABLED**            | **Enable garbage collection**<br>Enables or disables garbage collection (GC).                                                     | `true`         | `bool`  |
| **PHLOW_GARBAGE_COLLECTION_INTERVAL_SECONDS**   | **Garbage collection interval (seconds)**<br>Defines the interval at which garbage collection will be performed.                  | `60`           | `u64`   |
| **PHLOW_LOG**                                   | **Log level**<br>Defines the log verbosity for standard logging output. Possible values typically include `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`. | `WARN` | `str`   |
| **PHLOW_SPAN**                                  | **Span level**<br>Defines the verbosity level for span (OpenTelemetry) tracing. Possible values typically include `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`. | `INFO` | `str`   |
| **PHLOW_OTEL**                                  | **Enable OpenTelemetry**<br>Enables or disables OpenTelemetry tracing and metrics.                                                | `true`         | `bool`  |

---

### Notes

- If an environment variable is not set, the default value indicated in the table above will be used.
- Set the corresponding environment variables before running the application to override the defaults.
- The **log level** (`PHLOW_LOG`) and **span level** (`PHLOW_SPAN`) control different layers of logging:
  - `PHLOW_LOG`: Affects standard logging (e.g., error, warning, info messages).
  - `PHLOW_SPAN`: Affects tracing spans (useful for deeper telemetry insights with OpenTelemetry).
- The `PHLOW_OTEL` variable controls whether or not OpenTelemetry providers (for both tracing and metrics) are initialized.

---

## πŸ“œ License

MIT Β© 2025 β€” Built with ❀️ and Rust.