Commit fc52491

Eric Bower  ·  2026-05-09 12:12:00 -0400 EDT
parent d43b626
docs: site
1 files changed,  +153, -45
+153, -45
......@@ -255,6 +255,27 @@ h2 {
255255 font-weight: 700;
256256 margin-bottom: 1rem;
257257 color: var(--text);
258+ display: flex;
259+ align-items: center;
260+ gap: 0.5rem;
261+}
262+
263+h2 .anchor {
264+ color: var(--text-dim);
265+ opacity: 0;
266+ transition: opacity 0.15s ease;
267+ font-weight: 400;
268+ font-size: 0.85em;
269+ text-decoration: none;
270+}
271+
272+h2:hover .anchor {
273+ opacity: 1;
274+}
275+
276+h2 .anchor:hover {
277+ color: var(--green);
278+ text-decoration: none;
258279 }
259280
260281 h3 {
......@@ -497,6 +518,7 @@ code {
497518 <div class="logo">pici <span>— terminal-first CI</span></div>
498519 <ul>
499520 <li><a href="#how">How It Works</a></li>
521+ <li><a href="#infra">Infra</a></li>
500522 <li><a href="#features">Features</a></li>
501523 <li><a href="#beta">Beta</a></li>
502524 </ul>
......@@ -638,9 +660,10 @@ code {
638660 and on the CI runner</strong>. No YAML, no DSL, no "it works on my machine" gap.
639661 </p>
640662 <p>
641- <strong>zmx</strong> is the job engine. Each <code>zmx run</code> spawns a parallel terminal session.
642- <code>zmx wait "*"</code> blocks until they all finish. Run tasks sequentially by just calling them
643- one after another, or in parallel; it's bash, you control the flow.
663+ <a href="https://zmx.sh"><strong>zmx</strong></a> is the job engine. Each <code>zmx run</code> spawns
664+ a parallel terminal session. <code>zmx wait "*"</code> blocks until they all finish.
665+ Run tasks sequentially by just calling them one after another, or in parallel; it's bash,
666+ you control the flow.
644667 </p>
645668 <ul class="check-list">
646669 <li>Same <code>zmx run</code> commands locally and in CI</li>
......@@ -743,58 +766,124 @@ code {
743766 <p style="text-align:center; margin-top: 1.5rem; color: var(--text-dim);">
744767 Same <code>pico.sh</code>. Same <code>zmx attach</code>. Same experience. Your infra or ours.
745768 </p>
769+ <div style="text-align:center; margin-top: 1rem;">
770+ <span style="background: var(--surface); padding: 0.3rem 0.8rem; border-radius: 4px; font-size: 0.85rem; color: var(--yellow);">
771+ Bring your own isolation — docker, namespaces, bare metal. You choose.
772+ </span>
773+ </div>
774+ </div>
775+</section>
776+
777+<!-- ============================================================
778+ POWERED BY deploy.pico.sh
779+ ============================================================ -->
780+<section id="infra">
781+ <div class="container">
782+ <p class="section-label">Infrastructure</p>
783+ <h2>Powered by deploy.pico.sh</h2>
784+ <div class="two-col">
785+ <div>
786+ <p>
787+ <strong>ci.pico.sh</strong> runs on <strong>deploy.pico.sh</strong> — our SSH VM service
788+ on hardware we own. Not AWS. Not GCP. Not a cloud provider.
789+ </p>
790+ <p>
791+ Push a <code>docker-compose.yml</code> to an SSH endpoint and your containers are live.
792+ Label a service and it gets a public HTTP URL. No cloud console, no CLI SDK,
793+ no provider lock-in.
794+ </p>
795+ <p>
796+ The same platform that runs your CI runs your apps.
797+ </p>
798+ <ul class="check-list">
799+ <li>Our hardware — not a hyperscaler</li>
800+ <li>Docker Compose via <code>git push</code></li>
801+ <li>Expose HTTP services with compose labels</li>
802+ <li>Limited inventory — when it's gone, it's gone</li>
803+ </ul>
804+ </div>
805+ <div class="terminal">
806+ <div class="terminal-bar">
807+ <span class="dot red"></span>
808+ <span class="dot yellow"></span>
809+ <span class="dot green"></span>
810+ <span style="margin-left: 0.5rem;">deploy.pico.sh</span>
811+ </div>
812+ <div class="terminal-body">
813+ <div><span class="comment">--- docker-compose.yml ---</span></div>
814+ <div>services:</div>
815+ <div> api:</div>
816+ <div> image: myapp:latest</div>
817+ <div> labels:</div>
818+ <div> pico.http.expose: "true"</div>
819+ <div> pico.http.host: "api.myapp.pico.sh"</div>
820+ <div>&nbsp;</div>
821+ <div><span class="prompt">$ </span><span class="cmd">git remote add deploy ssh://deploy.pico.sh/myapp</span></div>
822+ <div><span class="prompt">$ </span><span class="cmd">git push deploy main</span></div>
823+ <div>&nbsp;</div>
824+ <div><span class="info">→ containers deployed</span></div>
825+ <div><span class="info">→ api.myapp.pico.sh is live</span></div>
826+ </div>
827+ </div>
828+ </div>
746829 </div>
747830 </section>
748831
749832 <!-- ============================================================
750- FEATURES GRID
833+ FEATURES
751834 ============================================================ -->
752835 <section id="features">
753836 <div class="container">
754837 <p class="section-label">Features</p>
755- <h2>Everything else just works.</h2>
838+ <h2>Built for terminals, agents, and humans.</h2>
756839 <div class="feature-grid">
757840 <div class="feature-card">
758- <div class="icon">🔌</div>
759- <h3>SSH-First</h3>
760- <p>No HTTP APIs. No webhooks to configure. No OAuth tokens. SSH keys are your auth, SSH pubsub is your event bus.</p>
761- </div>
762- <div class="feature-card">
763- <div class="icon">📊</div>
764- <h3>JSONL Status Stream</h3>
765- <p><code>pici monitor | curl -sd"$line" $WEBHOOK</code> — pipe build status to Discord, Slack, or any endpoint.</p>
766- </div>
767- <div class="feature-card">
768- <div class="icon">🧹</div>
769- <h3>Auto-Cancel on Push</h3>
770- <p>New commit? The old job is killed automatically. No stale builds wasting resources.</p>
841+ <div class="icon">🤖</div>
842+ <h3>AI-Agent Friendly</h3>
843+ <p>
844+ Terminal workflows, rsync + SSH pubsub, JSONL status streams — everything an agent
845+ needs to trigger builds, monitor progress, and attach to failures. No OAuth flows,
846+ no API rate limits, no webhooks. An agent can start as many jobs as it wants
847+ and read results as structured text.
848+ </p>
771849 </div>
772850 <div class="feature-card">
773- <div class="icon">🗑️</div>
774- <h3>Auto Garbage Collection</h3>
775- <p>Old sessions cleaned up automatically. No manual cleanup scripts.</p>
851+ <div class="icon">🔌</div>
852+ <h3>SSH-First</h3>
853+ <p>
854+ No HTTP APIs to expose. No webhooks to configure. No OAuth tokens to manage.
855+ SSH keys are your auth. SSH pubsub is your event bus.
856+ If SSH works, CI works.
857+ </p>
776858 </div>
777859 <div class="feature-card">
778- <div class="icon">📦</div>
779- <h3>Live Artifacts</h3>
780- <p>HTML session pages generated during the build, not just after. See progress in real-time.</p>
860+ <div class="icon">📄</div>
861+ <h3>Static Site Artifacts</h3>
862+ <p>
863+ Build artifacts are plain HTML + CSS. No JavaScript. No app server. No build step.
864+ Serve the directory with any static host — nginx, s3, pgs.sh, <code>python -m http.server</code>.
865+ Zero runtime dependencies.
866+ </p>
781867 </div>
782868 <div class="feature-card">
783869 <div class="icon">🔐</div>
784870 <h3>Build Attestation</h3>
785- <p>Automatic provenance: runner hostname, OS, arch, repo, branch, commit, workspace checksum.</p>
871+ <p>
872+ Automatic provenance baked into every job: runner hostname, OS, arch, repo, branch,
873+ commit, and workspace checksum. Supply-chain ready without extra tooling.
874+ </p>
786875 </div>
787876 </div>
788877 </div>
789878 </section>
790879
791880 <!-- ============================================================
792- COMPARISON — Other CI vs pici
881+ COMPARISON
793882 ============================================================ -->
794883 <section id="compare">
795884 <div class="container">
796885 <p class="section-label">Comparison</p>
797- <h2>Other CI gives you logs. pici gives you a shell.</h2>
886+ <h2>The difference is the terminal.</h2>
798887 <div class="code-compare">
799888 <div>
800889 <div class="label bad">Other CI</div>
......@@ -806,15 +895,16 @@ code {
806895 <span style="margin-left: 0.5rem;">build log</span>
807896 </div>
808897 <div class="terminal-body">
809- <div class="output">$ npm test</div>
810- <div class="output">PASS src/utils.test.js</div>
811- <div class="error">FAIL src/db.test.js</div>
812- <div class="error"> ● connects to database</div>
813- <div class="error"> TimeoutError: connect ETIMED OUT</div>
898+ <div class="output">$ go test ./...</div>
899+ <div class="output">ok myapp/db 12.4s</div>
900+ <div class="error">FAIL myapp/api (14.2s)</div>
901+ <div class="error">Error: connection refused</div>
814902 <div class="output">...</div>
815- <div class="comment"># ...that's all you get.</div>
816- <div class="comment"># Re-run the job? Wait 12 min.</div>
817- <div class="comment"># Enable "debug mode"? Where's that?</div>
903+ <div>&nbsp;</div>
904+ <div class="comment"># ← that's all you get</div>
905+ <div class="comment"># ← can't inspect the environment</div>
906+ <div class="comment"># ← can't rerun just the failing step</div>
907+ <div class="comment"># ← re-run entire pipeline? wait 10 min</div>
818908 </div>
819909 </div>
820910 </div>
......@@ -825,21 +915,21 @@ code {
825915 <span class="dot red"></span>
826916 <span class="dot yellow"></span>
827917 <span class="dot green"></span>
828- <span style="margin-left: 0.5rem;">zmx attach ci.myrepo.test</span>
918+ <span style="margin-left: 0.5rem;">zmx attach ci.myrepo.api</span>
829919 </div>
830920 <div class="terminal-body">
831- <div class="output">$ npm test</div>
832- <div class="output">PASS src/utils.test.js</div>
833- <div class="error">FAIL src/db.test.js</div>
834- <div class="error"> ● connects to database</div>
835- <div class="error"> TimeoutError: connect ETIMED OUT</div>
921+ <div class="output">$ go test ./...</div>
922+ <div class="output">ok myapp/db 12.4s</div>
923+ <div class="error">FAIL myapp/api (14.2s)</div>
924+ <div class="error">Error: connection refused</div>
836925 <div>&nbsp;</div>
837- <div><span class="comment"># you're already in the session</span></div>
838- <div><span class="prompt">$ </span><span class="cmd">env | grep DB_HOST</span></div>
926+ <div class="comment"># you're in the session — inspect freely</div>
927+ <div><span class="prompt">$ </span><span class="cmd">cat .env</span></div>
839928 <div class="output">DB_HOST=postgres.internal:5432</div>
840929 <div><span class="prompt">$ </span><span class="cmd">nc -zv postgres.internal 5432</span></div>
841- <div class="output">Connection refused</div>
842- <div><span class="comment"># ↑ found it — network config issue</span></div>
930+ <div class="error">Connection refused</div>
931+ <div><span class="comment"># ↑ found it — wrong hostname</span></div>
932+ <div><span class="comment"># ↑ press ↑ to rerun the test after fixing</span></div>
843933 </div>
844934 </div>
845935 </div>
......@@ -882,6 +972,24 @@ code {
882972 </footer>
883973
884974 <script>
975+// Auto-generate anchor links for all h2 elements
976+document.querySelectorAll('h2').forEach(h2 => {
977+ const section = h2.closest('section, [id]');
978+ if (section && section.id) {
979+ const a = document.createElement('a');
980+ a.href = '#' + section.id;
981+ a.className = 'anchor';
982+ a.textContent = '#';
983+ a.title = 'Copy link to section';
984+ a.addEventListener('click', e => {
985+ e.preventDefault();
986+ history.replaceState(null, null, '#' + section.id);
987+ window.scrollTo({ top: h2.offsetTop - 80, behavior: 'smooth' });
988+ });
989+ h2.appendChild(a);
990+ }
991+});
992+
885993 function handleSignup(e) {
886994 e.preventDefault();
887995 const form = e.target;