Why Not Just Bash or PowerShell?
Most teams already have “automation”:
- a pile of Bash or PowerShell scripts
- glued together with SSH, cron and CI jobs
So a natural question is:
Why do I need
flwdat all? Why not just write good Bash/PowerShell?
flwd does not replace your shell. It treats Bash and PowerShell as
runtimes and moves the boring, fragile parts into the engine: arguments,
safety, observability, profiles and tests.
This page walks through where raw scripts hurt, and how flwd changes the model.
1. Unsafe defaults and quoting foot-guns
Bash and PowerShell give you a lot of power with very little ceremony. That is exactly why small mistakes are easy and expensive.
Example: spaces in filenames
A classic Bash loop:
#!/usr/bin/env bash
set -e
for f in $(ls /backup); do
echo "Processing $f"
# ...
doneWorks until there is a file called db backup.tar.gz:
$fbecomesdband thenbackup.tar.gz- behaviour depends on current data
- tests might never exercise the broken case
The “defensive” version has to remember a lot:
#!/usr/bin/env bash
set -Eeuo pipefail
IFS=$'\n\t'
for f in /backup/*; do
[ -f "$f" ] || continue
echo "Processing: $f"
# ...
doneNow consider how many scripts in a typical environment:
- forget
set -Eeuo pipefail - forget to reset
IFS - forget to quote variables
Each author has to know and remember all of this, all the time.
How flwd helps
With flwd:
- job configuration is described declaratively (inputs, profiles, sources)
- the engine starts shells in a strict, controlled environment
- argument binding is driven by a typed ArgSpec, not ad-hoc
$1,$2
You still write the loop in Bash, but:
- strict mode and environment setup live in one place (the adapter + engine)
- the same behaviour is exercised by tests and by real runs
Individual authors focus on what the job does, not on memorising shell defensive patterns.
2. Every script re-implements argument parsing and help
To make a script pleasant to use, you typically need:
- flags and positional arguments
--helpoutput- validation (required, enums, ranges)
- shell completion (if you are lucky)
In raw Bash that usually looks like a custom parser:
ENV=""
RETRIES=3
while [[ $# -gt 0 ]]; do
case "$1" in
--env)
ENV="$2"; shift 2;;
--retries)
RETRIES="$2"; shift 2;;
--help|-h)
usage; exit 0;;
*)
echo "Unknown argument: $1" >&2; exit 1;;
esac
done
[ -n "$ENV" ] || { echo "--env is required" >&2; exit 1; }Problems:
- copy-pasted and tweaked differently in each script
- validation is inconsistent or missing
- help text drifts out of sync with actual behaviour
PowerShell has richer parameter support, but you still need to:
- declare parameters and validation attributes
- keep docs, scripts and CI in sync by hand
How flwd changes this
With flwd:
- inputs are described once in ArgSpec inside the job configuration
- the engine generates:
- consistent CLI flags
--helpoutput- shell completion
- validation errors before the script starts
You stop writing custom parsers and usage blocks. You describe:
argspec:
args:
- name: env
type: string
required: true
enum: [prod, staging, dev]
- name: retries
type: int
default: 3and flwd handles the rest.
3. Logging, audit and observability
Ad-hoc scripts typically log via echo, sometimes redirected to a file:
- no structured format
- no correlation ID per run
- no consistent mapping to metrics or health checks
It is hard to answer:
- “Who ran this job?”
- “With which arguments?”
- “What exactly happened between 03:12:05 and 03:12:10?”
Runs as first-class objects
flwd treats each execution as a run, not just a process:
- runs have IDs and status
- events (start, step, error, completion) are recorded
- logs can be streamed or exported
- metrics and health endpoints are exposed by the server mode
This gives you:
- a journal you can query
- Prometheus-friendly metrics
- a clear view of what your automation is actually doing
You do not have to re-invent log prefixes and ad-hoc log files in every script. The engine provides the envelope.
4. No profiles or invariants in raw scripts
Plain scripts run with “whatever they get”:
- current user and group
- ambient capabilities
- full network access unless constrained externally
- full filesystem tree unless constrained externally
There is no standard way to say:
- “this job must run rootless with dropped caps and no-new-privileges”
- “fail closed if we cannot enforce that”
flwd profiles and secure execution
flwd introduces profiles:
secure- rootless containers
- dropped capabilities
no_new_privileges- controlled network and filesystem
- fail-closed if invariants cannot be guaranteed
permissive- more flexible, still structured
disabled- full freedom, explicitly chosen
The engine inspects the container/runtime configuration and refuses to run under the secure profile if the environment does not match the contract.
Policy and invariants are centralised in the engine, not scattered across scripts.
5. Jobs as software with tests
Most organisations intend to test their scripts. In practice, testing is:
- bespoke CI jobs calling scripts with different arguments
- no consistent layout for tests and fixtures
- hard to share or reuse job logic safely
flwd jobs are designed to be testable
The flwd model (in the planned 1.0 design) is:
- jobs live alongside their tests and configuration
flwd :initcan scaffold a job + testsflwd :testcan run:- on the host
- in a dedicated QA container image
The same adapters, profiles and bindings are used for:
- local development
- CI
- production runs
Instead of re-inventing a test harness per script, you adopt a standard way to verify jobs.
6. What flwd actually does for you
Putting it together:
With raw Bash/PowerShell, each script has to handle:
- argument parsing and
--help - validation and enums
- environment setup (strict modes, locale, PATH, IFS…)
- logging and audit trail
- security profiles and capabilities
- testing and CI integration
With flwd, the engine provides:
- ArgSpec → flags,
--help, completion, validation - profiles → secure/permissive/disabled, enforced at runtime
- runs → IDs, journal, events, metrics
- adapters → consistent behaviour across Bash and PowerShell
- hooks →
:init,:testand QA images (planned for 1.0)
You still write small shell scripts. The difference is:
you focus on what the job should do, and let flwd handle the plumbing, safety, and observability.
When that trade-off makes sense for you, flwd is the right tool. When it does not, you can still “just” write a script — but now you have a clear picture of what you are giving up.