The Tugboat DevX tool
Last updated:NOTE:If you only looking for the code, jump to Source code
Context
Welcome
This project was written at General Atomics (GA), and is the property of GA. Thanks to the generosity of GA, a subset of the code is available to review. I attempted to provide perhaps too much background information because so many parts of the code are not available to review. However, I hope what I did provide can give you an understanding of how I structure code.
This project (and previous iterations) were almost entirely my own work. Some LLMs were used, and I estimate about 5% of the code is written by LLM (after review, of course). A few other edits here and there were provided by team members, but largely this is my project and should show how I thought about architecture and design. There is a weakness in that the lack of contributions means that there is almost certainly much room for improvement, but I also feel that this project is a very fair perspective on my software development maturity and where I am currently (2026) at in my career.
If you want to jump straight to the code, go to The Project.
Why this exists
tugboat is the latest iteration of a development and compliance tool used when packaging up releases to be shipped to the customer site.
Most deployments that use this deploy to classified (air gapped) environments where automated deploys are forbidden and which have an increased amount of cybersecurity compliance such as:
-
all non-public code must have static code analysis (SAST) scans
-
all containers must have dependency analysis performed
-
all vulnerabilities over a certain score (depends on the customer) must be mitigated, eliminated, or deliberately accepted
There are two critical needs that this project provides for:
-
Enumerating the transitive dependency tree for projects that ship 10-20 different containers, some with 10+ proprietary Java jars embedded including:
-
deduplication, specific to each dependency version and profile (when the same version of a jar can be compiled in different ways),
-
and the source code (git repo and commit sha) for each component.
-
-
Collecting all vulnerabilities and cross-referencing each found vulnerability with previously justified or accepted vulnerabilities.
Obviously, the faster a team can get through all these requirements the more quickly we can iterate and the more cheaply (less staff and/or less days to do compliance) they can develop.
For example, teams are free to tweak container images (remove code, update base images) much more often and get feedback of how that adjusted the cybersecurity posture now that they can generate these reports in just a matter of seconds.
Compliance vs security
This project aims to solve the compliance problem over the security problem. Security is hard to quantify, it can be dynamic (adversaries are not static), and it requires accepting many unknowns. Compliance and security frameworks help manage that, but speaking as a previous cybersecurity professional, there is a difference. Ultimately the customer requires compliance so we provide compliance.
History
This is the third (or thereabouts) major revision, because as different parts of this problem were solved, more teams wanted to take advantage of it and the more often it would be needed to be run.
It started as a series of bash scripts and a lot of manual labor that took ~10 ish hours to complete. Only a few individuals were even qualified to do that work (like me). Every run would reveal additional or different dependency chains, so it was re-architected into a python project.
The python project worked better now that dependency trees got a couple levels deep, but it still suffered from name/version collisions, non-deterministic results, and a large degree of non-portability (only ever worked on 3 different developer laptops). I hosted an architectural review with other senior devs across the company before re-writing it in Rust.
I chose Rust because I was somewhat familiar in it and I knew it could produce a statically compiled binary (solving the portability problem). This project essentially is about merging and referencing many different kinds of metadata, so using Rust enabled a higher quality code base due to stricter typing, algebraic data types, and more compile time guarantees. Aggressive caching, deterministic results, integration to other systems are major features that were designed for. Over the last 6 months or so, requests for additional major features have been completely supported by this architecture so it appears that the re-write was a success. Third time is the charm!
The Project
A redacted version of this project is on codeberg. However, the following sections provide additional information that can help fill in some of the gaps.
README
The documentation is a weak point in this project, but it may answer questions you have: README.adoc.
Original structure
Only some code can be provided, so the rest of the project is described here.
Tree
Here is the tree view of the original codebase.
63M fededge-cli 60 ├──.gitignore 5778 ├──.gitlab-ci.yml 1068 ├──bacon.toml 95K ├──Cargo.lock 2158 ├──Cargo.toml 66K ├──config.toml 41K ├──containers 12K │ ├──fededge-cli 1931 │ │ ├──Dockerfile 1891 │ │ └──justfile 12K │ ├──maven_yq 587 │ │ ├──Dockerfile 1317 │ │ └──justfile 12K │ └──trivy_lz4_oras 456 │ ├──Dockerfile 1332 │ └──justfile 66K ├──examples 477 │ ├──debug.yaml 1421 │ ├──example_kind_config.yaml 49K │ ├──jobs 2131 │ │ ├──git_clone.yaml 6193 │ │ ├──hostpath_test.yaml 3420 │ │ ├──list_images.yaml 2008 │ │ ├──maven_list_dependencies.yaml 5476 │ │ ├──read_write_many_test.yaml 3396 │ │ ├──README.adoc 1692 │ │ └──trivy_scan.yaml 900 │ └──README.adoc 4431 ├──justfile 23K ├──README.adoc 160 ├──rustfmt.toml 16K ├──source_map.toml 1.1M ├──src 164K │ ├──bin 31K │ │ ├──edgecli.rs 127K │ │ └──tugboat 14K │ │ ├──cli.rs 70K │ │ ├──command 25K │ │ │ ├──clone.rs 2666 │ │ │ ├──dependency.rs 2183 │ │ │ ├──list.rs 79 │ │ │ ├──mod.rs 17K │ │ │ ├──scan.rs 4370 │ │ │ └──spider.rs 5546 │ │ ├──context.rs 28K │ │ └──main.rs 922K │ └──lib 27K │ ├──cache.rs 9785 │ ├──common_cli.rs 115K │ ├──config 84 │ │ ├──mod.rs 9369 │ │ ├──pattern.rs 22K │ │ ├──project.rs 44K │ │ ├──source_map.rs 1606 │ │ ├──target.rs 18K │ │ └──user.rs 30K │ ├──errors.rs 1631 │ ├──generic.rs 230 │ ├──lib.rs 66K │ ├──local 6766 │ │ ├──exec.rs 464 │ │ ├──mod.rs 4450 │ │ ├──outcome.rs 1627 │ │ ├──parallel.rs 4564 │ │ ├──resource_monitor.rs 25K │ │ └──solve.rs 21K │ ├──oci.rs 176K │ ├──processing 139K │ │ ├──dependencies 5875 │ │ │ ├──bom.rs 12K │ │ │ ├──container.rs 21K │ │ │ ├──cyclonedx.rs 16K │ │ │ ├──git.rs 44K │ │ │ ├──graph.rs 22K │ │ │ ├──maven.rs 280 │ │ │ └──mod.rs 6352 │ │ ├──kubernetes_parsing.rs 1807 │ │ ├──list_images.rs 3529 │ │ ├──mod.rs 16K │ │ └──output.rs 238K │ ├──remote 115K │ │ ├──cluster 53K │ │ │ ├──components.rs 26K │ │ │ ├──connection.rs 19K │ │ │ ├──helpers.rs 4709 │ │ │ └──mod.rs 675 │ │ ├──definitions.rs 317 │ │ ├──mod.rs 10K │ │ ├──oci.rs 15K │ │ ├──solve.rs 73K │ │ ├──solve_slop.rs 4635 │ │ └──task.rs 119K │ ├──spec 25K │ │ ├──docker 1765 │ │ │ ├──mod.rs 14K │ │ │ └──schema.rs 946 │ │ ├──filters.rs 9891 │ │ ├──git.rs 4236 │ │ ├──helmfile_template.rs 20K │ │ ├──maven 2131 │ │ │ ├──cyclonedx.rs 227 │ │ │ ├──mod.rs 5165 │ │ │ └──pom.rs 1485 │ │ ├──mod.rs 1704 │ │ ├──syft.rs 37K │ │ └──trivy 14K │ │ ├──mod.rs 956 │ │ ├──parse.rs 10K │ │ └──schema.rs 49K │ ├──task 5538 │ │ ├──context.rs 8870 │ │ ├──definition.rs 334 │ │ ├──mod.rs 19K │ │ └──tracker.rs 758 │ ├──uptime.rs 37K │ ├──utils.rs 6958 │ └──version_check.rs 66K ├──templates 8192 │ ├──graph 1050 │ │ └──graph.mmd.jinja 53K │ └──task 49K │ └──spec 212 │ ├──docker_inspect.sh.jinja 352 │ ├──git_clone.sh.jinja 376 │ ├──helmfile_template.sh.jinja 379 │ ├──mvn_cyclonedx.sh.jinja 352 │ ├──mvn_effective_pom.sh.jinja 561 │ ├──README.adoc 422 │ ├──syft_cyclonedx.sh.jinja 376 │ ├──trivy_db_info.sh.jinja 492 │ ├──trivy_fetch_specific_db.sh.jinja 663 │ ├──trivy_image.sh.jinja 591 │ └──trivy_latest_db.sh.jinja 266K └──tests 2361 ├──cache.rs 8246 ├──config_project.rs 2013 ├──config_user.rs 70K ├──edgecli 3298 │ ├──clone_maven_project.rs 2286 │ ├──completion.rs 4096 │ ├──fixtures 725 │ ├──help.rs 1272 │ ├──helpers.rs 21K │ ├──k8s_integration.rs 2476 │ ├──list.rs 536 │ ├──main.rs 7642 │ ├──trivy.rs 1264 │ └──verify.rs 4096 ├──fixtures 29K ├──local.rs 4679 ├──oci.rs 7066 ├──processing.rs 2258 ├──remote.rs 8962 ├──task.rs 4038 ├──tracker.rs 5070 ├──trivy.rs 90K └──tugboat 23K ├──clone.rs 7208 ├──dependencies.rs 3219 ├──filter_images.rs 12K ├──fixtures 175 │ ├──filter_images_input0.yaml 4075 │ └──filter_images_input1.yaml 1405 ├──help.rs 1272 ├──helpers.rs 2624 ├──init.rs 1665 ├──list.rs 195 ├──main.rs 8068 ├──spider.rs 5982 └──trivy.rs
Code Size
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Language Files Lines Code Comments Blanks ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ AsciiDoc 4 721 534 0 187 Dockerfile 3 82 62 5 15 Jinja2 11 152 131 19 2 Just 4 300 185 48 67 Markdown 1 86 0 70 16 TOML 5 1705 1287 207 211 YAML 10 918 858 59 1 ───────────────────────────────────────────────────────────────────────────────── Rust 98 26508 21571 2375 2562 |- Markdown 77 2299 68 1951 280 (Total) 28807 21639 4326 2842 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Total 136 32771 24696 4734 3341 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
(as measured by tokei)
Help text
This is what you get when running tugboat --help, lightly redacted:
Locally executed helmfile-based project developent utility
Usage: tugboat [OPTIONS] <COMMAND>
Commands:
init Initialize the cache and config. Automatically called when needed.
list List information about a deployment
blake3hash Compute a blake3 hash of a file
filter-images Filter kubernetes yaml to extract images present
trivy Runs trivy vulnerability scanner
set-trivy-db Download a specific Trivy db to your local machine
clone Clone the source repo(s) for one or more artifacts
verify Verify that the installed tugboat binary is correctly configured
dependencies Follow and list all the transitive dependencies of an artifact [aliases: deps, dependency]
spider Follow the transitive dependencies of every artifact in a project
completion Generate completion for your shell
help Print this message or the help of the given subcommand(s)
Options:
-v, --verbose...
Turn debugging information on
-c, --cache <CACHE>
Set the location of the cache, defaults to `$XDG_DIR/.cache/edge`. Can also be set with the environment variable `EDGE_CACHE`. Command line flag takes precidence over environment variable
--clean
Clean the cache, cluster, etc so this command can start from a "fresh slate"
--timeout <TIMEOUT>
Set the individual task timeout (rather than the default of 10 min). Because timeouts are handled on a task-by-task basis, the overall run of the binary may result in an effective timeout of some multiple of this value. Accepts a string that gets parsed into a duration.
Usage:
--timeout 30s
--timeout 5m10s
-m, --local-source-map
Use `$PWD/source_map.toml` as artifact source mapping configuration file. This file maps Maven, OCI/Docker artifacts to their git repository sources when pom.xml has incorrect or missing SCM information
--no-color
Prevent the tool from writing colorful ANSI output
--skip-update-check
Skip checking the server for a newer release of this tool for this run. To disable the check without CLI flags, set `check_for_updates = false` in your user config. This CLI flag will override whatever is set in your config file
-h, --help
Print help (see a summary with '-h')
-V, --version
Print version
Examples:
cat templated.yaml | tugboat filter-images | xargs docker pull
tugboat trivy [proj] table image example.com/.../debian:bookworm-slim --db-date 2026-01-01
tugboat dependencies --filtered -o graph.mmd mvn com.example:artifact:24.0.0:jar mermaid
tugboat dependencies --filtered -o graph.text image example.com/.../example:5.10.1 text
tugboat spider [proj]
tugboat spider -f -m [proj] plain | tugboat clone -m --clone-in ./repos
tugboat spider -f -m [proj] plain | tugboat clone --dry-run
tugboat clone -m --clone-to ./my-artifact mvn com.example:artifact:22.0.1
tugboat set-trivy-db 2026-06-29
Source code
There are 4 modules available to read:
-
processing- this is where metadata is merged, cross-referenced, etc. This is the main "business logic" module. -
local- this is how tasks (which are templated bash scripts) get executed (solved), cached, etc.-
errors- common error definitions. -
generic- traits.
-
The README provides information on other aspects of the codebase, especially about how caching is handled.