-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patharcvix-logic-driven-container-security.tex
More file actions
1567 lines (1298 loc) · 61.3 KB
/
Copy patharcvix-logic-driven-container-security.tex
File metadata and controls
1567 lines (1298 loc) · 61.3 KB
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
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
% SPDX-License-Identifier: MPL-2.0
% arcvix-logic-driven-container-security.tex
%
% Academic paper describing Stapeln's approach to deterministic container
% security via miniKanren logic programming. Systems/HCI crossover.
%
% Author: Jonathan D.A. Jewell
% Date: 2026-03-21
\documentclass[11pt,twocolumn]{article}
%% ---- Packages ----
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{amsmath,amssymb,amsfonts}
\usepackage{graphicx}
\usepackage{booktabs}
\usepackage{hyperref}
\usepackage{xcolor}
\usepackage{listings}
\usepackage{algorithm}
\usepackage{algpseudocode}
\usepackage{natbib}
\usepackage{balance}
\usepackage{microtype}
\usepackage{url}
\usepackage{enumitem}
\usepackage{subcaption}
\usepackage{xspace}
\usepackage[margin=0.75in]{geometry}
%% ---- Custom commands ----
\newcommand{\stapeln}{\textsc{Stapeln}\xspace}
\newcommand{\minikanren}{\textsc{miniKanren}\xspace}
\newcommand{\kanren}{\textsc{Kanren}\xspace}
\newcommand{\owasp}{\textsc{OWASP}\xspace}
\newcommand{\cve}{\textsc{CVE}\xspace}
%% ---- Listings configuration ----
\lstset{
basicstyle=\ttfamily\small,
breaklines=true,
frame=single,
numbers=left,
numberstyle=\tiny\color{gray},
keywordstyle=\color{blue!70!black}\bfseries,
commentstyle=\color{green!50!black}\itshape,
stringstyle=\color{red!60!black},
showstringspaces=false,
tabsize=2,
captionpos=b,
aboveskip=8pt,
belowskip=8pt,
}
\lstdefinelanguage{Scheme}{
morekeywords={define,lambda,let,cond,if,begin,set!,fresh,conde,run,run*,==,use-modules,quote},
morecomment=[l]{;},
morestring=[b]",
sensitive=true
}
\lstdefinelanguage{Elixir}{
morekeywords={defmodule,def,defp,do,end,fn,case,cond,when,if,else,true,false,nil,alias,use},
morecomment=[l]{\#},
morestring=[b]",
sensitive=true
}
%% ---- Title ----
\title{%
Deterministic Container Security via Logic Programming:\\
A \minikanren Approach to Infrastructure Reasoning%
}
\author{
Jonathan D.A. Jewell\\
\texttt{j.d.a.jewell@open.ac.uk}\\
Independent Researcher
}
\date{March 2026}
\begin{document}
\maketitle
%% ==================================================================
%% ABSTRACT
%% ==================================================================
\begin{abstract}
Container orchestration has become the dominant paradigm for deploying
networked services, yet its security tooling remains largely heuristic:
scanners pattern-match against known signatures, producing non-deterministic
results that vary across runs and offer limited explainability. We present
\stapeln, a visual container stack designer that replaces heuristic scanning
with a \emph{deterministic} security reasoning engine built on \minikanren,
a relational logic programming language embedded in Guile Scheme. Security
properties---port conflict freedom, network isolation, privilege
minimisation, protocol safety---are expressed as logical relations and
verified via constraint propagation \emph{before} any container is
instantiated. The system generates a full provenance chain for every
finding, enabling auditability that satisfies compliance frameworks
(OWASP, CIS Benchmarks, NIST~800-190). Equally central to the design is
an accessibility thesis: that infrastructure configuration should be
approachable by non-expert users without sacrificing security rigour.
\stapeln realises this through a game-like visual interface with real-time
security scoring, one-click auto-fixes, and undo-everywhere semantics,
targeting a benchmark where a reasonably IT-capable twelve-year-old can
build a secure container stack. We describe the architecture
(ReScript-TEA frontend, Elixir/Phoenix backend, Elixir-native \minikanren
core, Rust code generation, Idris2 formal proofs), detail the logic
engine's rule system and gap analysis pipeline, compare deterministic
reasoning against heuristic scanners, and discuss implications for
infrastructure democratisation through formal methods.
\end{abstract}
%% ==================================================================
%% 1. INTRODUCTION
%% ==================================================================
\section{Introduction}
\label{sec:introduction}
The containerisation revolution, initiated by Docker in 2013 and
accelerated by Kubernetes, has fundamentally changed how software is
deployed. Yet this revolution has produced a paradox: while containers
were originally pitched as a simplification---``build once, run
anywhere''---the operational reality involves hundreds of CLI flags,
YAML indentation errors, obscure networking modes, and security
footguns that even experienced engineers routinely trigger.
The consequences are measurable. Sysdig's 2025 Container Security
Report found that 76\% of running containers had known critical or high
vulnerabilities, and that 65\% of container images in production
registries had not been scanned at all~\cite{sysdig2025}. The
National Vulnerability Database lists thousands of container-related
CVEs annually, many of which reduce to trivially detectable
misconfigurations: exposed database ports, containers running as root,
unencrypted traffic on public interfaces, absent health checks.
Why do these persist? We identify two root causes:
\begin{enumerate}[leftmargin=*]
\item \textbf{Heuristic, non-deterministic security tooling.}
Mainstream container scanners (Trivy, Grype, Snyk Container)
operate by pattern-matching against signature databases. Their
results can vary across runs, they cannot guarantee completeness
over a configuration space, and they provide limited
\emph{explanations} for their findings. An operator told ``high
severity: CVE-2024-XXXXX'' must independently research the
relevance to their specific stack.
\item \textbf{Expert-only interfaces.} The entire container
ecosystem---Docker CLI, docker-compose YAML, Kubernetes
manifests, Helm charts---assumes fluency with systems
administration concepts. There is no on-ramp for non-experts,
despite the fact that small businesses, educators, hobbyists,
and students increasingly need to deploy containerised services.
\end{enumerate}
This paper presents \stapeln\footnote{Swedish for ``the stack,''
pronounced \textipa{/\textprimstress sta\textlengthmark p\textschwa ln/}.},
a system that addresses both problems simultaneously. Its contributions are:
\begin{enumerate}[leftmargin=*]
\item A \textbf{deterministic security reasoning engine} based on
\minikanren~\cite{byrd2010,friedman2005} that expresses security
properties as logical relations, verifies them via unification
and constraint propagation, and produces machine-auditable
provenance chains for every finding.
\item A \textbf{pre-deployment gap analysis pipeline} that analyses the
\emph{entire} stack topology before any container is instantiated,
detecting port conflicts, network isolation violations, privilege
escalation risks, and configuration vulnerabilities at design time.
\item A \textbf{visual, game-like interface} that makes security
visible through real-time scoring (0--100), severity badges, and
one-click auto-fixes, targeting the benchmark that a reasonably
IT-capable twelve-year-old can build a secure container stack
without prior container knowledge.
\item An \textbf{accessibility-first architecture} achieving WCAG~2.3
Level~AAA compliance, with keyboard navigation, screen reader
support, Braille annotations, and motion sensitivity across all
interaction modes.
\end{enumerate}
We argue that this combination---formal methods for security reasoning,
visual interaction for accessibility---constitutes a new design point
in the infrastructure tooling space. Rather than ``dumbing down''
containers, \stapeln democratises them through rigour: the same logic
engine that makes the system accessible to novices also makes it more
trustworthy than expert-oriented heuristic scanners.
%% ==================================================================
%% 2. BACKGROUND
%% ==================================================================
\section{Background}
\label{sec:background}
\subsection{Container Orchestration and Its Discontents}
\label{sec:bg-containers}
A container packages an application and its dependencies into an
isolated runtime environment, sharing the host kernel via Linux
namespaces and cgroups. The Open Container Initiative (OCI)
standardises image and runtime specifications~\cite{oci2024}, and
container engines---Docker, Podman, containerd, nerdctl---implement
these specifications with varying security postures.
Configuration complexity grows combinatorially with stack size. A
three-tier web application (reverse proxy, application server,
database) requires specifying: images and tags, port mappings (host to
container), volume mounts with permission semantics, network topology,
environment variables (often containing secrets), health check
parameters, resource limits, user/group IDs, capability sets, SELinux
or AppArmor profiles, and restart policies. Each dimension admits
security-relevant misconfiguration. The standard representation format
is YAML (for docker-compose) or JSON (for Kubernetes manifests), both
of which are error-prone for non-trivial stacks.
Podman~\cite{podman2024} offers a significant security improvement
over Docker by running rootless by default, eliminating the daemon
attack surface, and providing per-container SELinux labelling.
\stapeln uses Podman as its backend runtime and generates
``Containerfiles'' (not Dockerfiles) to align with OCI-neutral
terminology.
\subsection{Logic Programming and miniKanren}
\label{sec:bg-kanren}
Logic programming represents computation as the search for values that
satisfy logical relations. Prolog~\cite{colmerauer1993} pioneered this
paradigm; \minikanren~\cite{byrd2010,friedman2005} distils it to a
minimal core that can be embedded in any host language:
\begin{itemize}[leftmargin=*]
\item \texttt{==} (unification): Constrain two terms to be equal.
\item \texttt{fresh}: Introduce new logic variables.
\item \texttt{conde}: Disjunction---try multiple clauses.
\item \texttt{conj}: Conjunction---require all goals to succeed.
\item \texttt{run}/\texttt{run*}: Execute a query, returning
substitutions.
\end{itemize}
These five primitives suffice for Turing-complete relational
programming~\cite{byrd2017}. A \minikanren program \emph{relates}
inputs to outputs rather than computing directionally; the same
relation can be queried forwards (``given this configuration, what
vulnerabilities exist?'') or backwards (``given this vulnerability,
what configurations trigger it?''). This bidirectionality is crucial
for both detection and remediation.
Implementations exist for Scheme~\cite{friedman2005},
Racket~\cite{racketkanren}, Clojure (core.logic~\cite{corelogic}),
Haskell~\cite{kiselyov2005}, and Python. \stapeln implements
\minikanren natively in Elixir (Section~\ref{sec:engine-impl}),
leveraging the BEAM virtual machine's lightweight process model for
concurrent query evaluation.
\subsection{Formal Methods for Infrastructure}
\label{sec:bg-formal}
Formal verification of infrastructure configurations is an active
research area. HashiCorp Sentinel~\cite{sentinel2024} provides
policy-as-code for Terraform, using a domain-specific language.
Open Policy Agent (OPA)~\cite{opa2024} uses Rego, a Datalog-derived
query language, for admission control in Kubernetes. Cue~\cite{cue2024}
and Dhall~\cite{dhall2024} offer type-safe configuration languages.
Nickel~\cite{nickel2024} provides contracts and gradual typing for
configuration.
These tools operate at the \emph{policy enforcement} layer: they
define constraints and reject configurations that violate them. \stapeln
operates at the \emph{reasoning} layer: it not only detects violations
but explains \emph{why} they are violations, traces the reasoning
chain to authoritative sources (OWASP, CIS, CVE), suggests specific
remediations, and can synthesise safe configurations via backward
querying. This distinction---enforcement versus reasoning---is the
key differentiator.
%% ==================================================================
%% 3. THE ACCESSIBILITY THESIS
%% ==================================================================
\section{The Accessibility Thesis}
\label{sec:accessibility}
\subsection{Motivation}
\label{sec:acc-motivation}
The \stapeln project is guided by a single benchmark:
\begin{quote}
\emph{A twelve-year-old who is reasonably IT-capable can help their
parents build a secure container stack.}
\end{quote}
This is deliberately provocative. The intent is not to trivialise
infrastructure security but to establish a \emph{ceiling on accidental
complexity}. If the interface requires reading 500 pages of Docker
documentation, it has failed---not because documentation is bad, but
because the abstraction layer is wrong. The complexity budget should
be spent on \emph{essential} complexity (security decisions,
architectural choices) rather than \emph{accidental} complexity
(YAML syntax, CLI flags, networking jargon).
\subsection{Design Principles}
\label{sec:acc-principles}
Six principles guide \stapeln's interaction design:
\begin{enumerate}[leftmargin=*]
\item \textbf{Zero prerequisites.} No prior container knowledge
required. The system explains concepts in context.
\item \textbf{Visual primacy.} Every abstraction has a visual
representation. Networks are lines, volumes are file picker
dialogs, ports are numbered slots.
\item \textbf{Conversational errors.} Error messages explain the
problem in plain language, state why it matters, and offer
one-click fixes.
\item \textbf{Undo everywhere.} Every action is reversible. A
timeline slider enables ``go back to 5 minutes ago'' rollback.
\item \textbf{Secure by default.} Smart defaults (rootless, network
isolation, resource limits, read-only root filesystem) mean
that the \emph{easiest path is the secure path}.
\item \textbf{Progressive disclosure.} Advanced options exist but
are hidden behind ``Advanced'' toggles. The default view shows
only what is needed.
\end{enumerate}
\subsection{WCAG 2.3 AAA Compliance}
\label{sec:acc-wcag}
\stapeln targets the highest level of the Web Content Accessibility
Guidelines. Key measures include:
\begin{itemize}[leftmargin=*]
\item 7:1 colour contrast ratios (AAA minimum) in both light and dark
modes, with 21:1 achieved for primary text.
\item Full keyboard navigation with visible 3px focus indicators.
\item ARIA landmarks, labels, and live regions for screen reader
compatibility with NVDA, JAWS, VoiceOver, and TalkBack.
\item Braille-ready annotations on all interactive elements, including
component name, type, state, and position.
\item Respect for \texttt{prefers-reduced-motion} to disable
animations for users with vestibular disorders.
\item Text in \texttt{rem} units supporting 400\% zoom without
horizontal scrolling.
\end{itemize}
Accessibility is not an afterthought bolted onto a developer tool;
it is the \emph{primary design constraint}. We contend that
accessibility and security are mutually reinforcing: a system that
clearly communicates security state to sighted users can, with proper
engineering, communicate it equally clearly to users of assistive
technology.
%% ==================================================================
%% 4. MINIKANREN SECURITY ENGINE
%% ==================================================================
\section{The miniKanren Security Engine}
\label{sec:engine}
\subsection{Architecture Overview}
\label{sec:engine-arch}
The security engine sits between the frontend (which captures user
intent as a stack model) and the deployment backend (which generates
Containerfiles and orchestration manifests). Its position is
deliberate: \emph{all} security reasoning occurs at design time,
before any container is instantiated.
\begin{figure}[t]
\centering
\small
\begin{verbatim}
+-----------------+ +------------------+
| ReScript-TEA | | Knowledge Base |
| Frontend | | (Scheme rules, |
| (stack model) | | CVE/OWASP feed) |
+--------+--------+ +--------+---------+
| |
v v
+-------------------------------------------+
| miniKanren Core (Elixir) |
| Unification | Constraint propagation |
| Bidirectional query | Provenance chain |
+-------------------+-----------------------+
|
+----------+-----------+
| |
v v
+----------------+ +------------------+
| Gap Analysis | | Remediation |
| (violations + | | Synthesis |
| provenance) | | (backward query) |
+----------------+ +------------------+
\end{verbatim}
\caption{Security engine data flow. The stack model and knowledge base
are inputs; gap analysis and remediation are outputs.}
\label{fig:engine-arch}
\end{figure}
\subsection{Stack Model as Logical Facts}
\label{sec:engine-model}
The user's container stack is encoded as a set of logical facts
amenable to \minikanren querying. Each service becomes a \emph{component}
term with associated properties:
\begin{lstlisting}[language=Scheme,caption={Stack model as logical
facts (Guile Scheme representation).},label=lst:stack-model]
(define example-stack
'((component nginx-1 web-server
(user . root)
(port 80 public)
(port 443 public)
(health-check . #nil))
(component postgres-1 database
(user . postgres)
(port 5432 internal)
(health-check . (interval 30)))
(component svalinn-1 gateway
(user . gateway)
(port 8443 public)
(health-check . (interval 10)))))
\end{lstlisting}
Each component is a tuple \texttt{(component \emph{id type}
\emph{properties...})} where properties are association pairs. The
\minikanren engine treats this structure as a database of ground
facts against which security rules are evaluated.
\subsection{Security Properties as Logical Relations}
\label{sec:engine-rules}
Security properties are expressed as \minikanren relations---goals
that succeed when a violation is present. We describe the four
fundamental relations; the production system includes 47 rules
covering OWASP Top~10, CIS Docker Benchmark, and NIST~800-190.
\subsubsection{Privilege Escalation Detection}
\label{sec:rule-root}
The relation \texttt{running-as-root$_o$} succeeds for any component
whose user property is \texttt{root} or undefined (which defaults to
root in OCI runtimes):
\begin{lstlisting}[language=Scheme,caption={Root container detection
as a logical relation.},label=lst:root-rule]
(define (running-as-rooto component)
(fresh (user)
(componento component)
(container-usero component user)
(conde
[(== user 'root)]
[(== user #nil)])))
\end{lstlisting}
Querying \texttt{(run* (c) (running-as-rooto c))} against the stack
in Listing~\ref{lst:stack-model} returns \texttt{(nginx-1)},
deterministically identifying the single root container. The relation
is tagged with severity \texttt{critical} and linked to CIS~Benchmark
4.1 and CVE-2019-5736 (runc container breakout via root).
\subsubsection{Port Conflict Detection via Constraint Propagation}
\label{sec:rule-ports}
Port conflicts are detected by the \texttt{port-conflict$_o$} relation,
which unifies pairs of components binding the same host port:
\begin{lstlisting}[language=Scheme,caption={Port conflict detection.},
label=lst:port-conflict]
(define (port-conflicto c1 c2 port)
(fresh (iface1 iface2)
(=/= c1 c2)
(exposed-porto c1 port iface1)
(exposed-porto c2 port iface2)))
\end{lstlisting}
The disequality constraint \texttt{(=/= c1 c2)} ensures we do not
match a component against itself. Querying \texttt{(run* (c1 c2 p)
(port-conflicto c1 c2 p))} returns all conflicting pairs with the
contested port number, enabling the UI to highlight both components
and the specific port.
\subsubsection{Network Isolation Verification}
\label{sec:rule-network}
Network reachability is modelled as a transitive relation:
\begin{lstlisting}[language=Scheme,caption={Network reachability as a
transitive closure.},label=lst:network-reach]
(define (reachableo a b)
(conde
[(directly-connectedo a b)]
[(fresh (mid)
(directly-connectedo a mid)
(reachableo mid b))]))
(define (isolation-violationo c)
(fresh (external)
(componento c)
(internal-onlyo c)
(external-networko external)
(reachableo external c)))
\end{lstlisting}
This detects cases where a component marked as internal-only (e.g., a
database) is reachable from an external network through transitive
connections. The transitive closure terminates because the component
set is finite and each recursive step introduces a fresh intermediate
node that must exist in the knowledge base.
\subsubsection{Protocol Safety Analysis}
\label{sec:rule-protocol}
A lookup relation maps ports to protocols and flags unsafe protocols
on public interfaces:
\begin{lstlisting}[language=Scheme,caption={Protocol safety relation.},
label=lst:protocol-rule]
(define (unencrypted-traffico component)
(fresh (port protocol interface)
(componento component)
(exposed-porto component port interface)
(== interface 'public)
(protocolo port protocol)
(conde
[(== protocol 'http)]
[(== protocol 'ftp)]
[(== protocol 'telnet)]
[(== protocol 'smtp)])))
\end{lstlisting}
This is linked to OWASP~A02:2021 (Cryptographic Failures) and
NIST~800-52r2 (TLS~1.2+ requirement for public endpoints).
\subsection{Provenance Chains}
\label{sec:engine-provenance}
Every finding produced by the engine includes a \emph{provenance chain}:
an ordered list of reasoning steps that connects the finding to
authoritative sources. For example, the root container finding for
\texttt{nginx-1} produces:
\begin{enumerate}[leftmargin=*,noitemsep]
\item Component \texttt{nginx-1} has \texttt{(user~.~root)}.
\item Source: user's stack configuration.
\item Rule triggered: \texttt{running-as-root$_o$}.
\item Authority: CIS Docker Benchmark~4.1---``Ensure a user for the
container has been created.''
\item CVE reference: CVE-2019-5736 (runc container breakout via root).
\item Authority: NIST NVD.
\item Conclusion: \textbf{Critical} severity.
\end{enumerate}
Provenance chains serve three audiences: end users (who click ``Why?''
in the UI), compliance auditors (who require traceable reasoning), and
the remediation engine (which uses the chain to synthesise fixes).
\subsection{Bidirectional Querying for Remediation Synthesis}
\label{sec:engine-bidir}
Because \minikanren relations are bidirectional, the same rules used
for detection can be run ``backwards'' to synthesise safe
configurations. Given the relation \texttt{running-as-root$_o$}, we
can query:
\begin{lstlisting}[language=Scheme,caption={Backward query for
remediation synthesis.},label=lst:backward]
;; "What user values do NOT trigger
;; the root-container rule?"
(run* (user)
(fresh (component)
(componento component)
(container-usero component user)
(absento user '(root #nil))))
\end{lstlisting}
The result is the set of non-root, non-empty user values already
present in the stack, which the auto-fix engine can suggest as
replacements. This is fundamentally different from heuristic scanners,
which detect problems but cannot \emph{reason about solutions}.
%% ==================================================================
%% 5. GAP ANALYSIS: PRE-DEPLOYMENT VERIFICATION
%% ==================================================================
\section{Gap Analysis: Pre-Deployment Verification}
\label{sec:gap}
\subsection{The Gap Analysis Pipeline}
\label{sec:gap-pipeline}
\stapeln's gap analysis runs whenever the stack model changes---on
every drag, connection, or configuration edit---and produces a
continuously updated security assessment. The pipeline consists of
four stages:
\begin{enumerate}[leftmargin=*]
\item \textbf{Model extraction.} The ReScript frontend serialises the
current stack model as JSON and sends it to the Elixir backend
via WebSocket.
\item \textbf{Fact generation.} The backend converts the JSON model
into \minikanren facts (the component/property tuples of
Section~\ref{sec:engine-model}).
\item \textbf{Rule evaluation.} All security rules are evaluated
against the facts. Each rule returns a (possibly empty) set of
violations with provenance chains.
\item \textbf{Scoring and presentation.} Violations are aggregated
into a security score (0--100), sorted by severity, and pushed
to the frontend via WebSocket for real-time display.
\end{enumerate}
\subsection{Security Scoring}
\label{sec:gap-scoring}
The security score is computed as a weighted deduction from a perfect
baseline:
\begin{equation}
\label{eq:score}
S = \max\!\left(0,\; 100 - \sum_{v \in V} w(v) \cdot \text{severity}(v)\right)
\end{equation}
where $V$ is the set of detected violations, $w(v)$ is a
category-specific weight (e.g., privilege escalation weighs more
heavily than missing health checks), and $\text{severity}(v) \in
\{10, 8, 5, 2, 0\}$ maps critical/high/medium/low/info to numeric
scores via a \minikanren relation (Listing~\ref{lst:severity}).
\begin{lstlisting}[language=Elixir,caption={Severity scoring as a
\minikanren relation in Elixir.},label=lst:severity]
def severity_score(severity, score) do
Core.conde([
[Core.eq(severity, :critical),
Core.eq(score, 10)],
[Core.eq(severity, :high),
Core.eq(score, 8)],
[Core.eq(severity, :medium),
Core.eq(score, 5)],
[Core.eq(severity, :low),
Core.eq(score, 2)],
[Core.eq(severity, :info),
Core.eq(score, 0)]
])
end
\end{lstlisting}
The score is displayed as a large, colour-coded badge in the UI:
green (80--100), yellow (50--79), orange (25--49), red (0--24). This
gamification---reminiscent of credit scores or video game health
bars---makes security state immediately legible to non-experts.
\subsection{Simulation Mode}
\label{sec:gap-simulation}
Before deployment, users can enter simulation mode, which animates
packet flow through the stack topology. The simulation kernel runs in
WebAssembly (compiled from Rust) and models:
\begin{itemize}[leftmargin=*]
\item TCP connection establishment between services.
\item DNS resolution within container networks.
\item Firewall rule evaluation at network boundaries.
\item TLS handshake presence/absence on public interfaces.
\end{itemize}
Packets are rendered as animated dots flowing along connection lines
in the Cisco View (Section~\ref{sec:visual-cisco}). Blocked packets
flash red; successful connections flash green. This visual feedback
makes network isolation tangible: users can \emph{see} that the
database is unreachable from the internet, rather than trusting an
abstract policy statement.
\subsection{Auto-Fix Engine}
\label{sec:gap-autofix}
Each violation includes one or more remediation options, ranked by
safety:
\begin{enumerate}[leftmargin=*,noitemsep]
\item \textbf{Recommended fix}: the safest, most conservative option
(e.g., change user from root to \texttt{appuser:1000}).
\item \textbf{Alternative fix}: a less conservative option that may
have trade-offs (e.g., add \texttt{CAP\_NET\_BIND\_SERVICE}
instead of changing the port).
\item \textbf{Dismiss with justification}: the user can dismiss
a finding with a text explanation, which is recorded in the
provenance log for audit purposes.
\end{enumerate}
The ``Auto-Fix All'' button applies all recommended fixes
simultaneously, with a preview diff shown before confirmation and
full undo support.
%% ==================================================================
%% 6. VISUAL INTERACTION DESIGN
%% ==================================================================
\section{Visual Interaction Design}
\label{sec:visual}
\stapeln provides four complementary views of the same underlying stack
model, each optimised for a different cognitive task.
\subsection{Paragon View: Supply Chain Hierarchy}
\label{sec:visual-paragon}
The Paragon View presents the stack as a vertical block layout
inspired by GParted's partition editor. Each layer of the supply
chain---from the base image builder (Chainguard/distroless) through
the container runtime to the application---is a horizontal block whose
width indicates resource consumption. A sidebar displays the gap
analysis findings in real time, with colour-coded severity indicators
and one-click navigation to the offending component.
\subsection{Cisco View: Network Topology}
\label{sec:visual-cisco}
The Cisco View is a drag-and-drop canvas (implemented as an SVG
surface with infinite pan and zoom) where components are nodes and
connections are edges. Users drag components from a palette and draw
connection lines between them. Connections represent network
reachability; the \minikanren engine validates every connection as it
is drawn, immediately flagging isolation violations.
Components are rendered with distinct shapes: rectangles for services,
ovals for databases, pentagons for gateways, and nested containers
for multi-service pods. Port mappings appear as numbered slots on
component edges. Network interfaces appear as coloured lines: blue
for internal, red for public, green for encrypted.
\subsection{Lago Grey Designer: Base Image Construction}
\label{sec:visual-lago}
The Lago Grey view is a visual minimal Linux distribution designer.
Users select packages from an interactive catalogue, and the system
calculates the resulting image size in real time with competitive
comparisons (e.g., ``14.6~MB vs 60~MB Alpine''). Packages are
categorised by size: Floes ($<$1~MB), Icebergs (1--75~MB), Glaciers
($>$75~MB). Security indicators show post-quantum and classical
cryptographic stack coverage.
\subsection{Settings and Configuration}
\label{sec:visual-settings}
A settings view exposes \stapeln's own configuration with the same
visual conventions used for container configuration: toggles for
security defaults (rootless mode, signature verification, SBOM
requirements), theme selection, and export format preferences. All
settings persist to local storage and synchronise across tabs.
\subsection{Game-Like UX: Security as Stats}
\label{sec:visual-game}
The deliberate use of game mechanics---scores, badges, progress
bars, colour-coded health indicators---draws on research in
gamification for security awareness~\cite{deCaro2024,
scholefield2019}. The security score is not hidden in a log file;
it is the most prominent element on screen, updating in real time as
the user modifies the stack. This inversion of visibility---from
``security is an audit concern'' to ``security is a first-class
design metric''---is central to the accessibility thesis.
%% ==================================================================
%% 7. DETERMINISTIC VS HEURISTIC SECURITY REASONING
%% ==================================================================
\section{Deterministic vs.\ Heuristic Security Reasoning}
\label{sec:comparison}
Table~\ref{tab:comparison} summarises the structural differences
between \stapeln's \minikanren engine and conventional heuristic
scanners.
\begin{table*}[t]
\centering
\caption{Comparison of deterministic (miniKanren) and heuristic
(signature-based) security reasoning.}
\label{tab:comparison}
\small
\begin{tabular}{@{}lll@{}}
\toprule
\textbf{Property} & \textbf{miniKanren (Stapeln)} & \textbf{Heuristic Scanners} \\
\midrule
Determinism & Same input $\Rightarrow$ same output, always & May vary across runs \\
Explainability & Full provenance chain per finding & Typically CVE ID + severity only \\
Completeness & Complete over defined rule set & Dependent on signature database \\
Updateability & Add rule file; immediate effect & Requires database sync; may lag \\
Bidirectionality & Detect violations \emph{and} synthesise fixes & Detection only \\
Resource cost & $<$10~MB memory, millisecond latency & Moderate (100s of MB for databases) \\
Hallucination risk & Zero (logic is sound) & N/A (no generative component) \\
Compliance audit & Machine-readable provenance chain & Manual report generation \\
Pre-deployment & Analyses stack before instantiation & Typically scans running containers \\
\bottomrule
\end{tabular}
\end{table*}
\subsection{Formal Soundness}
\label{sec:comparison-sound}
\minikanren's operational semantics guarantee that if a relation
succeeds, the unifying substitution is a model of the relational
specification~\cite{byrd2017}. This means that every violation
reported by the engine is a \emph{true positive} with respect to the
rule set. False positives can only arise from rule
misspecification---a qualitatively different failure mode than the
false positives inherent in heuristic matching, which arise from
signature overgeneralisation.
The rule set itself can be validated: each rule is tagged with an
authoritative source (OWASP identifier, CIS Benchmark section, CVE
number), enabling external auditors to verify that the rule correctly
encodes the cited requirement.
\subsection{Limitations of the Deterministic Approach}
\label{sec:comparison-limits}
Deterministic reasoning is not a panacea:
\begin{itemize}[leftmargin=*]
\item \textbf{Novel vulnerabilities.} The engine can only detect
violations that are encoded as rules. Zero-day vulnerabilities
with no corresponding rule are invisible until a rule is written.
However, the fast rule-addition cycle (a single Scheme file)
mitigates this: rules can be deployed within minutes of CVE
publication.
\item \textbf{Semantic gaps.} The engine reasons about
\emph{configuration} properties, not \emph{runtime} behaviour.
It cannot detect, for example, a buffer overflow inside a
running application. This is by design: runtime scanning
(Falco, Sysdig) is complementary, not replaced.
\item \textbf{Rule set maintenance.} The rule set must be curated.
\stapeln addresses this through automated CVE feed
synchronisation (Section~\ref{sec:impl-cve}) and community
contribution via a rule repository.
\end{itemize}
%% ==================================================================
%% 8. IMPLEMENTATION
%% ==================================================================
\section{Implementation}
\label{sec:impl}
\subsection{System Architecture}
\label{sec:impl-arch}
\stapeln is implemented as a multi-language system:
\begin{description}[leftmargin=*]
\item[Frontend:] ReScript~\cite{rescript2024} with the TEA (The Elm
Architecture) pattern, compiled to JavaScript and served by Deno.
The SVG canvas, component palette, configuration panels, and
security score display are all ReScript modules.
\item[Backend:] Elixir~\cite{elixir2024} with Phoenix~\cite{phoenix2024}
for HTTP/WebSocket APIs. The \minikanren core
(Section~\ref{sec:engine-impl}), security rules, and gap analysis
pipeline are native Elixir modules.
\item[Code generation:] Rust~\cite{rust2024}, integrated via
Rustler~\cite{rustler2024} NIFs (Native Implemented Functions), for
generating Containerfiles, docker-compose YAML, and Podman compose
TOML. Rust's affine type system provides compile-time guarantees
that volumes are consumed exactly once and port bindings are
conflict-free at the generation level.
\item[Formal proofs:] Idris2~\cite{brady2021} for compile-time
verification of invariants that the \minikanren engine enforces at
runtime: stack acyclicity, port uniqueness, and dependency DAG
well-formedness. These proofs are invoked via an Elixir Port
wrapper.
\item[Container runtime:] Podman, running rootless by default, with
Chainguard~\cite{chainguard2024} base images for minimal attack
surface.
\end{description}
\subsection{miniKanren Core in Elixir}
\label{sec:engine-impl}
The \minikanren core is implemented as a pure-Elixir module
(\texttt{Stapeln.Kanren.Core}) with no external dependencies. The
implementation comprises approximately 190 lines of code and provides:
\begin{itemize}[leftmargin=*]
\item Logic variables as tagged structs (\texttt{\%Core\{id: n\}}).
\item A substitution map (\texttt{\%\{lvar $\Rightarrow$ term\}}).
\item Walk (shallow) and walk-deep (recursive) for substitution
traversal.
\item Unification over atoms, lists, and tuples with occurs-check
omitted for performance (safe in our application domain where
terms are acyclic by construction).
\item Goal combinators: \texttt{eq}, \texttt{conj}, \texttt{disj},
\texttt{all}, \texttt{conde}, \texttt{fresh}.
\item Stream operations (\texttt{bind}, \texttt{mplus}) with
interleaving search~\cite{byrd2010} for fairness.
\item \texttt{run}/\texttt{run\_fresh} interfaces returning reified
results.
\end{itemize}
The choice to implement \minikanren in Elixir rather than calling
an external Guile Scheme process has three advantages:
\begin{enumerate}[leftmargin=*]
\item \textbf{No IPC overhead.} Rules execute in-process on the
BEAM VM, avoiding serialisation/deserialisation latency.
\item \textbf{Concurrency.} Each security analysis spawns as a
lightweight BEAM process, enabling parallel evaluation of
independent rule groups.
\item \textbf{Hot code reloading.} New rules can be deployed without
restarting the application, leveraging Elixir's OTP release
mechanisms.
\end{enumerate}
Listing~\ref{lst:elixir-core} shows the unification implementation.
\begin{lstlisting}[language=Elixir,caption={Unification in the Elixir
miniKanren core.},label=lst:elixir-core]
def unify(u, v, s) do
u = walk(u, s)
v = walk(v, s)
cond do
u == v -> s
lvar?(u) -> Map.put(s, u, v)
lvar?(v) -> Map.put(s, v, u)
is_tuple(u) and is_tuple(v)
and tuple_size(u) == tuple_size(v) ->
unify(Tuple.to_list(u),
Tuple.to_list(v), s)
is_list(u) and is_list(v)
and length(u) == length(v) ->
Enum.zip(u, v)
|> Enum.reduce_while(s, fn {a,b}, acc ->
case unify(a, b, acc) do
nil -> {:halt, nil}
s2 -> {:cont, s2}
end
end)
true -> nil
end
end
\end{lstlisting}
\subsection{Security Rules Module}
\label{sec:impl-rules}
Security rules are implemented as Elixir functions in
\texttt{Stapeln.Kanren.SecurityRules} that compose \minikanren goals.
The module provides:
\begin{itemize}[leftmargin=*]
\item \texttt{vuln\_exposed\_db/3}: Relates ports to database
exposure vulnerabilities (PostgreSQL~5432, MySQL~3306,
MongoDB~27017, Redis~6379).
\item \texttt{vuln\_root\_container/3}: Detects containers running
as root.
\item \texttt{vuln\_no\_healthcheck/3}: Flags services without
health check configuration.
\item \texttt{vuln\_default\_creds/3}: Detects services using
default credentials.
\item \texttt{port\_protocol/3}: Maps ports to protocols and risk
levels.
\item \texttt{severity\_score/2}: Maps severity atoms to numeric
scores.
\end{itemize}
Each rule is a pure function returning a \minikanren goal. Rules are
composed via \texttt{Core.conde} (disjunction across vulnerability
types) and \texttt{Core.all} (conjunction of conditions). The query
interface (\texttt{query\_db\_vulnerabilities/0},
\texttt{query\_port\_info/1}, \texttt{query\_severity\_scores/0})
wraps goal evaluation in \texttt{Core.run\_fresh/2} and returns
Elixir-native data structures.
The high-level engine (\texttt{Stapeln.Kanren.Engine}) provides
\texttt{reason/1}, which accepts a stack map, extracts port lists,
runs all queries, and returns an enriched result map with findings,
counts, and severity distributions.
\subsection{CVE Feed Integration}
\label{sec:impl-cve}
A daily synchronisation job (implemented as a Guile Scheme script
invoked via cron) fetches the NIST NVD API~\cite{nvd2024}, filters
for container-relevant CVEs (by CPE matching against known container