PageSourceSearch

https://totallyprivate.ai/api

html totallyprivate.ai collected 2026-10-05 13:06:51 UTC 43,374 bytes, 1,395 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en">
3<head>
4<meta charset="UTF-8">
5<meta name="viewport" content="width=device-width, initial-scale=1.0">
6<title>Totally Private AI — Developer API</title>
7<meta name="description" content="OpenAI-compatible API inside your VPN. Same SDK, same tools, no public internet. Air-gapped AI infrastructure for developers building with sensitive data.">
8
9<!-- Favicon -->
10<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='6' fill='%230b1221'/%3E%3Cpath d='M16 5C11.6 5 8 8.1 8 12v3c-1.1 0-2 .9-2 2v8c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2v-8c0-1.1-.9-2-2-2v-3c0-3.9-3.6-7-8-7zm0 2.5c3 0 5.5 2 5.5 4.5v3h-11v-3c0-2.5 2.5-4.5 5.5-4.5z' fill='%23254480'/%3E%3Ccircle cx='16' cy='21' r='2' fill='%23C9A227'/%3E%3C/svg%3E">
11<link rel="icon" type="image/png" sizes="32x32" href="assets/Clean Color Logo 2 small.png">
12
13<!-- Open Graph -->
14<meta property="og:type" content="website">
15<meta property="og:url" content="https://totallyprivate.ai/api">
16<meta property="og:title" content="Totally Private AI — Developer API">
17<meta property="og:description" content="OpenAI-compatible API inside your VPN. Same SDK, same tools, no public internet. Build agents and workflows on air-gapped AI infrastructure.">
18<meta property="og:image" content="https://totallyprivate.ai/assets/og-image.png">
19<meta property="og:site_name" content="Totally Private AI">
20
21<!-- Twitter Card -->
22<meta name="twitter:card" content="summary_large_image">
23<meta name="twitter:title" content="Totally Private AI — Developer API">
24<meta name="twitter:description" content="OpenAI-compatible API, air-gapped. Build on Claude via Bedrock without sending prompts over the public internet.">
25<meta name="twitter:image" content="https://totallyprivate.ai/assets/og-image.png">
26
27<!-- Preconnect -->
28<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
29
30<!-- Montserrat from jsDelivr -->
31<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/[email protected]/300.css">
32<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/[email protected]/400.css">
33<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/[email protected]/500.css">
34<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/[email protected]/700.css">
35<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/[email protected]/300-italic.css">
36<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/[email protected]/400-italic.css">
37<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/[email protected]/500-italic.css">
38
39<style>
40/* ============================================================
41   TPAI Design Tokens
42   ============================================================ */
43:root {
44  --tpai-blue:        #254480;
45  --tpai-blue-hover:  #2D5299;
46  --tpai-blue-10:     rgba(37, 68, 128, 0.10);
47  --tpai-gold:        #C9A227;
48  --tpai-gold-dark:   #A8871E;
49  --tpai-navy:        #0b1221;
50  --tpai-navy-light:  #0f1a2e;
51
52  --white:            #FFFFFF;
53  --off-white:        #F2F0EB;
54  --light-gray:       #E8E6E1;
55  --mid-gray:         #888780;
56  --dark-gray:        #3D3D3A;
57  --near-black:       #1A1A18;
58
59  --dark-text-primary:   #E8EDF4;
60  --dark-text-secondary: #8899B3;
61  --dark-text-muted:     #7A8DA6;
62  --dark-border:         #2A3A54;
63
64  --success:          #0F6E56;
65  --success-bg:       rgba(15, 110, 86, 0.10);
66  --danger:           #A32D2D;
67  --danger-bg:        rgba(163, 45, 45, 0.08);
68
69  --font-sans: 'Montserrat', system-ui, -apple-system, sans-serif;
70  --font-mono: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
71
72  --page-max: 1200px;
73  --page-gutter: 24px;
74
75  --shadow-card: 0 1px 3px rgba(0,0,0,0.08);
76  --shadow-card-hover: 0 4px 12px rgba(0,0,0,0.10);
77}
78
79/* ============================================================
80   Reset & Base
81   ============================================================ */
82*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
83
84html {
85  font-size: 16px;
86  -webkit-font-smoothing: antialiased;
87  -moz-osx-font-smoothing: grayscale;
88  scroll-behavior: smooth;
89}
90
91body {
92  font-family: var(--font-sans);
93  font-weight: 400;
94  color: var(--near-black);
95  line-height: 1.6;
96  background: var(--white);
97}
98
99img { max-width: 100%; height: auto; display: block; }
100a { color: inherit; text-decoration: none; }
101
102.wrap {
103  max-width: var(--page-max);
104  margin: 0 auto;
105  padding: 0 var(--page-gutter);
106}
107
108/* ============================================================
109   Overlines
110   ============================================================ */
111.overline {
112  font-family: var(--font-sans);
113  font-weight: 500;
114  font-size: clamp(14px, 1.5vw, 16px);
115  line-height: 1.2;
116  letter-spacing: 0.15em;
117  text-transform: uppercase;
118  margin-bottom: 16px;
119}
120.overline--gold { color: var(--tpai-gold); }
121.overline--blue { color: var(--tpai-blue); }
122
123/* ============================================================
124   Section headline
125   ============================================================ */
126.section-headline {
127  font-family: var(--font-sans);
128  font-weight: 700;
129  font-size: clamp(24px, 3.5vw, 32px);
130  line-height: 1.25;
131  letter-spacing: -0.01em;
132}
133
134/* ============================================================
135   Gold divider
136   ============================================================ */
137.gold-rule {
138  width: 80px;
139  height: 2px;
140  background: var(--tpai-gold);
141  border: none;
142  margin: 24px auto;
143}
144
145/* ============================================================
146   Buttons
147   ============================================================ */
148.btn {
149  font-family: var(--font-sans);
150  font-weight: 500;
151  font-size: 14px;
152  padding: 14px 28px;
153  border-radius: 8px;
154  cursor: pointer;
155  transition: all 0.2s ease;
156  display: inline-block;
157  border: none;
158  text-align: center;
159  white-space: nowrap;
160}
161.btn:active { opacity: 0.85; }
162
163.btn--primary {
164  background: var(--tpai-blue);
165  color: var(--white);
166}
167.btn--primary:hover { background: var(--tpai-blue-hover); }
168
169.btn--outline {
170  background: transparent;
171  color: var(--dark-text-primary);
172  border: 1px solid var(--dark-border);
173}
174.btn--outline:hover { background: var(--tpai-blue-10); }
175
176.btn--gold {
177  background: var(--tpai-gold);
178  color: var(--tpai-navy);
179  font-weight: 700;
180  padding: 16px 40px;
181  font-size: 15px;
182}
183.btn--gold:hover { background: #d4ad30; }
184
185.btn--outline-blue {
186  background: transparent;
187  color: var(--tpai-blue);
188  border: 2px solid var(--tpai-blue);
189  font-weight: 600;
190}
191.btn--outline-blue:hover { background: var(--tpai-blue-10); }
192
193/* ============================================================
194   NAV
195   ============================================================ */
196.site-nav {
197  position: fixed;
198  top: 0; left: 0; right: 0;
199  z-index: 100;
200  background: rgba(11, 18, 33, 0.92);
201  backdrop-filter: blur(12px);
202  -webkit-backdrop-filter: blur(12px);
203  border-bottom: 1px solid var(--dark-border);
204}
205.site-nav .wrap {
206  display: flex;
207  align-items: center;
208  justify-content: space-between;
209  height: 64px;
210}
211.nav-brand {
212  font-family: var(--font-sans);
213  font-weight: 700;
214  font-size: 18px;
215  color: var(--dark-text-primary);
216  display: flex;
217  align-items: center;
218  gap: 10px;
219}
220.nav-brand img { height: 40px; width: auto; }
221.nav-brand .g { color: var(--tpai-gold); }
222
223.nav-links {
224  display: flex;
225  align-items: center;
226  gap: 32px;
227  list-style: none;
228}
229.nav-links a {
230  font-weight: 500;
231  font-size: 15px;
232  color: var(--dark-text-secondary);
233  transition: color 0.2s ease;
234}
235.nav-links a:hover { color: var(--dark-text-primary); }
236
237.nav-cta {
238  font-weight: 500 !important;
239  color: var(--white) !important;
240  background: var(--tpai-blue);
241  padding: 10px 24px;
242  border-radius: 8px;
243  transition: background 0.2s ease;
244}
245.nav-cta:hover { background: var(--tpai-blue-hover) !important; }
246
247.nav-toggle {
248  display: none;
249  background: none;
250  border: none;
251  cursor: pointer;
252  padding: 4px;
253}
254.nav-toggle span {
255  display: block;
256  width: 24px;
257  height: 2px;
258  background: var(--dark-text-primary);
259  margin: 5px 0;
260  transition: all 0.2s ease;
261}
262
263/* ============================================================
264   CODE BLOCKS
265   ============================================================ */
266.code-block {
267  background: #080d17;
268  border: 1px solid var(--dark-border);
269  border-radius: 10px;
270  overflow: hidden;
271}
272.code-chrome {
273  display: flex;
274  align-items: center;
275  gap: 8px;
276  padding: 10px 16px;
277  background: rgba(15, 26, 46, 0.6);
278  border-bottom: 1px solid var(--dark-border);
279}
280.code-chrome-dot {
281  width: 8px;
282  height: 8px;
283  border-radius: 50%;
284}
285.code-chrome-dot:nth-child(1) { background: #ef4444; }
286.code-chrome-dot:nth-child(2) { background: #eab308; }
287.code-chrome-dot:nth-child(3) { background: #22c55e; }
288.code-chrome-label {
289  font-family: var(--font-mono);
290  font-size: 11px;
291  color: var(--dark-text-muted);
292  margin-left: 8px;
293}
294pre {
295  margin: 0;
296  padding: 20px 24px;
297  overflow-x: auto;
298  font-family: var(--font-mono);
299  font-size: 13px;
300  line-height: 1.7;
301  color: var(--dark-text-primary);
302  -webkit-overflow-scrolling: touch;
303}
304pre .comment { color: var(--dark-text-muted); }
305pre .string { color: #7ec699; }
306pre .key { color: #e8b87a; }
307pre .cmd { color: #c9a227; }
308pre .flag { color: #8899B3; }
309pre .url { color: #7aadce; }
310pre .kw { color: #c792ea; }
311pre .fn { color: #82aaff; }
312pre .op { color: #89ddff; }
313pre .num { color: #f78c6c; }
314
315/* ============================================================
316   HERO
317   ============================================================ */
318.hero {
319  background: var(--tpai-navy);
320  padding: 160px 0 80px;
321  position: relative;
322  overflow: hidden;
323}
324.hero::before {
325  content: '';
326  position: absolute;
327  inset: 0;
328  background: radial-gradient(ellipse 70% 60% at 60% -10%, rgba(37,68,128,0.22) 0%, transparent 70%);
329  pointer-events: none;
330}
331.hero::after {
332  content: '';
333  position: absolute;
334  bottom: 0;
335  left: 0;
336  right: 0;
337  height: 2px;
338  background: linear-gradient(90deg, transparent 10%, var(--tpai-gold) 50%, transparent 90%);
339  opacity: 0.35;
340}
341.hero .wrap { position: relative; z-index: 1; }
342
343.hero-grid {
344  display: grid;
345  grid-template-columns: 1fr 1fr;
346  gap: 56px;
347  align-items: center;
348}
349.hero-copy {
350  max-width: 520px;
351}
352
353.hero-overline {
354  font-family: var(--font-sans);
355  font-weight: 500;
356  font-size: clamp(14px, 1.6vw, 16px);
357  letter-spacing: 0.15em;
358  text-transform: uppercase;
359  color: var(--dark-text-primary);
360  margin-bottom: 24px;
361  opacity: 0.85;
362}
363
364.hero-headline {
365  font-family: var(--font-sans);
366  font-weight: 700;
367  font-size: clamp(28px, 4.5vw, 44px);
368  line-height: 1.1;
369  letter-spacing: -0.025em;
370  color: var(--white);
371  margin-bottom: 24px;
372}
373.hero-headline .gold { color: var(--tpai-gold); }
374
375.hero-sub {
376  font-family: var(--font-sans);
377  font-weight: 400;
378  font-size: clamp(15px, 1.8vw, 17px);
379  color: var(--dark-text-secondary);
380  line-height: 1.6;
381  margin-bottom: 32px;
382}
383
384.hero-buttons {
385  display: flex;
386  gap: 16px;
387  flex-wrap: wrap;
388}
389
390.hero-code {
391  max-width: 560px;
392}
393
394/* ============================================================
395   PROBLEM: PUBLIC vs PRIVATE API
396   ============================================================ */
397.problem {
398  background: var(--off-white);
399  padding: 96px 0;
400}
401.problem-header {
402  text-align: center;
403  margin-bottom: 48px;
404}
405.problem-header .section-headline { color: var(--near-black); }
406.problem-header .gold-accent { color: var(--tpai-gold-dark); }
407.problem-sub {
408  font-size: 16px;
409  color: var(--dark-gray);
410  margin-top: 12px;
411  line-height: 1.6;
412  max-width: 640px;
413  margin-left: auto;
414  margin-right: auto;
415}
416
417.versus-grid {
418  display: grid;
419  grid-template-columns: 1fr 1fr;
420  gap: 20px;
421  max-width: 920px;
422  margin: 0 auto;
423}
424.versus-card {
425  border-radius: 12px;
426  padding: 36px 32px;
427  position: relative;
428  overflow: hidden;
429}
430.versus-card::before {
431  content: '';
432  position: absolute;
433  top: 0; left: 0; right: 0;
434  height: 3px;
435}
436.versus-card--public {
437  background: var(--white);
438  border: 1px solid rgba(163, 45, 45, 0.2);
439}
440.versus-card--public::before { background: var(--danger); }
441.versus-card--private {
442  background: var(--white);
443  border: 1px solid rgba(15, 110, 86, 0.2);
444}
445.versus-card--private::before { background: var(--success); }
446
447.versus-tag {
448  display: inline-block;
449  font-size: 11px;
450  font-weight: 700;
451  text-transform: uppercase;
452  letter-spacing: 0.1em;
453  padding: 4px 12px;
454  border-radius: 100px;
455  margin-bottom: 20px;
456}
457.versus-tag--public {
458  background: var(--danger-bg);
459  color: var(--danger);
460  border: 1px solid rgba(163, 45, 45, 0.2);
461}
462.versus-tag--private {
463  background: var(--success-bg);
464  color: var(--success);
465  border: 1px solid rgba(15, 110, 86, 0.2);
466}
467
468.versus-card h3 {
469  font-size: 18px;
470  font-weight: 700;
471  margin-bottom: 16px;
472  line-height: 1.3;
473}
474.versus-card--public h3 { color: var(--danger); }
475.versus-card--private h3 { color: var(--success); }
476
477.versus-path {
478  font-family: var(--font-mono);
479  font-size: 12px;
480  color: var(--dark-gray);
481  line-height: 1.8;
482  background: var(--off-white);
483  border: 1px solid var(--light-gray);
484  border-radius: 8px;
485  padding: 16px;
486  margin-bottom: 16px;
487}
488.versus-path .red { color: var(--danger); font-weight: 600; }
489.versus-path .green { color: var(--success); font-weight: 600; }
490
491.versus-facts {
492  list-style: none;
493  display: flex;
494  flex-direction: column;
495  gap: 8px;
496}
497.versus-facts li {
498  font-size: 14px;
499  color: var(--dark-gray);
500  line-height: 1.55;
501  padding-left: 20px;
502  position: relative;
503}
504.versus-card--public .versus-facts li::before {
505  content: '\00D7';
506  position: absolute;
507  left: 0;
508  color: var(--danger);
509  font-weight: 800;
510  font-size: 14px;
511}
512.versus-card--private .versus-facts li::before {
513  content: '\2713';
514  position: absolute;
515  left: 0;
516  color: var(--success);
517  font-weight: 800;
518  font-size: 12px;
519  top: 2px;
520}
521
522/* ============================================================
523   HOW IT WORKS
524   ============================================================ */
525.how-it-works {
526  background: var(--tpai-navy);
527  padding: 96px 0;
528}
529.how-header {
530  text-align: center;
531  margin-bottom: 48px;
532}
533.how-header .section-headline { color: var(--white); }
534.how-header .gold-accent { color: var(--tpai-gold); }
535.how-sub {
536  font-size: 16px;
537  color: var(--dark-text-secondary);
538  margin-top: 12px;
539  line-height: 1.6;
540}
541
542.how-grid {
543  display: grid;
544  grid-template-columns: repeat(3, 1fr);
545  gap: 20px;
546  max-width: 920px;
547  margin: 0 auto 48px;
548}
549.how-card {
550  background: var(--tpai-navy-light);
551  border: 1px solid var(--dark-border);
552  border-radius: 12px;
553  padding: 32px 24px;
554  text-align: center;
555}
556.how-card-icon {
557  width: 56px;
558  height: 56px;
559  border-radius: 14px;
560  display: flex;
561  align-items: center;
562  justify-content: center;
563  margin: 0 auto 16px;
564  background: rgba(201, 162, 39, 0.10);
565  border: 1px solid rgba(201, 162, 39, 0.25);
566}
567.how-card-icon svg {
568  width: 28px;
569  height: 28px;
570  stroke: var(--tpai-gold);
571  fill: none;
572  stroke-width: 1.5;
573}
574.how-card h4 {
575  font-size: 15px;
576  font-weight: 700;
577  color: var(--dark-text-primary);
578  margin-bottom: 8px;
579}
580.how-card p {
581  font-size: 14px;
582  color: var(--dark-text-secondary);
583  line-height: 1.6;
584}
585
586/* Traffic path diagram */
587.traffic-path {
588  max-width: 920px;
589  margin: 0 auto;
590}
591.traffic-path .code-block {
592  border-color: rgba(15, 110, 86, 0.3);
593}
594
595/* ============================================================
596   DROP-IN COMPATIBLE
597   ============================================================ */
598.drop-in {
599  background: var(--white);
600  padding: 96px 0;
601}
602.drop-in-header {
603  text-align: center;
604  margin-bottom: 48px;
605}
606.drop-in-header .section-headline { color: var(--near-black); }
607.drop-in-header .gold-accent { color: var(--tpai-gold-dark); }
608.drop-in-sub {
609  font-size: 16px;
610  color: var(--dark-gray);
611  margin-top: 12px;
612  line-height: 1.6;
613}
614
615.drop-in-content {
616  display: grid;
617  grid-template-columns: 1fr 1fr;
618  gap: 48px;
619  max-width: 920px;
620  margin: 0 auto;
621  align-items: start;
622}
623
624.tools-grid {
625  display: grid;
626  grid-template-columns: 1fr 1fr;
627  gap: 12px;
628}
629.tool-card {
630  background: var(--off-white);
631  border: 1px solid var(--light-gray);
632  border-radius: 10px;
633  padding: 20px;
634  text-align: center;
635  transition: border-color 0.2s, box-shadow 0.2s;
636}
637.tool-card:hover {
638  border-color: var(--tpai-blue);
639  box-shadow: var(--shadow-card-hover);
640}
641.tool-card-name {
642  font-size: 14px;
643  font-weight: 700;
644  color: var(--near-black);
645  margin-bottom: 4px;
646}
647.tool-card-desc {
648  font-size: 12px;
649  color: var(--mid-gray);
650  line-height: 1.5;
651}
652.tool-card--any {
653  grid-column: 1 / -1;
654  background: var(--tpai-blue-10);
655  border-color: rgba(37, 68, 128, 0.2);
656}
657.tool-card--any .tool-card-name { color: var(--tpai-blue); }
658
659/* ============================================================
660   WHAT'S SUPPORTED
661   ============================================================ */
662.supported {
663  background: var(--off-white);
664  padding: 96px 0;
665}
666.supported-header {
667  text-align: center;
668  margin-bottom: 48px;
669}
670.supported-header .section-headline { color: var(--near-black); }
671.supported-header .gold-accent { color: var(--tpai-gold-dark); }
672
673.supported-grid {
674  display: grid;
675  grid-template-columns: repeat(3, 1fr);
676  gap: 20px;
677  max-width: 920px;
678  margin: 0 auto;
679}
680.supported-card {
681  background: var(--white);
682  border: 1px solid var(--light-gray);
683  border-radius: 12px;
684  padding: 32px 24px;
685}
686.supported-card h4 {
687  font-size: 15px;
688  font-weight: 700;
689  color: var(--near-black);
690  margin-bottom: 12px;
691}
692.supported-card p {
693  font-size: 14px;
694  color: var(--dark-gray);
695  line-height: 1.6;
696}
697.supported-card code {
698  font-family: var(--font-mono);
699  font-size: 12px;
700  background: var(--off-white);
701  border: 1px solid var(--light-gray);
702  padding: 2px 6px;
703  border-radius: 4px;
704  color: var(--tpai-blue);
705}
706
707.supported-note {
708  max-width: 920px;
709  margin: 32px auto 0;
710  background: var(--white);
711  border: 1px solid var(--light-gray);
712  border-left: 3px solid var(--tpai-gold);
713  border-radius: 0 10px 10px 0;
714  padding: 20px 24px;
715}
716.supported-note p {
717  font-size: 14px;
718  color: var(--dark-gray);
719  line-height: 1.6;
720}
721.supported-note strong { color: var(--near-black); }
722
723/* ============================================================
724   SECURITY COMPARISON
725   ============================================================ */
726.security {
727  background: var(--tpai-navy);
728  padding: 96px 0;
729}
730.security-header {
731  text-align: center;
732  margin-bottom: 48px;
733}
734.security-header .section-headline { color: var(--white); }
735.security-header .gold-accent { color: var(--tpai-gold); }
736
737.compare-table-wrap {
738  max-width: 920px;
739  margin: 0 auto;
740  overflow-x: auto;
741  -webkit-overflow-scrolling: touch;
742}
743.compare-table {
744  width: 100%;
745  border-collapse: separate;
746  border-spacing: 0;
747  font-size: 14px;
748}
749.compare-table thead th {
750  background: var(--tpai-navy-light);
751  color: var(--dark-text-primary);
752  font-weight: 700;
753  font-size: 13px;
754  text-transform: uppercase;
755  letter-spacing: 0.06em;
756  padding: 14px 20px;
757  text-align: left;
758  border-bottom: 2px solid var(--dark-border);
759}
760.compare-table thead th:first-child {
761  border-radius: 10px 0 0 0;
762}
763.compare-table thead th:last-child {
764  border-radius: 0 10px 0 0;
765}
766.compare-table thead th.col-tpai {
767  color: var(--tpai-gold);
768}
769.compare-table thead th.col-public {
770  color: var(--dark-text-muted);
771}
772.compare-table tbody td {
773  padding: 14px 20px;
774  border-bottom: 1px solid var(--dark-border);
775  color: var(--dark-text-secondary);
776  vertical-align: top;
777  line-height: 1.5;
778}
779.compare-table tbody td:first-child {
780  font-weight: 600;
781  color: var(--dark-text-primary);
782  white-space: nowrap;
783}
784.compare-table tbody tr:last-child td {
785  border-bottom: none;
786}
787.compare-table tbody tr:last-child td:first-child {
788  border-radius: 0 0 0 10px;
789}
790.compare-table tbody tr:last-child td:last-child {
791  border-radius: 0 0 10px 0;
792}
793.compare-table tbody td {
794  background: rgba(15, 26, 46, 0.3);
795}
796.compare-table .highlight {
797  color: var(--tpai-gold);
798  font-weight: 600;
799}
800.compare-table .dim {
801  color: var(--dark-text-muted);
802}
803
804/* ============================================================
805   CTA
806   ============================================================ */
807.cta-section {
808  background: var(--off-white);
809  padding: 96px 0;
810}
811.cta-inner {
812  max-width: 640px;
813  margin: 0 auto;
814  text-align: center;
815}
816.cta-inner .section-headline {
817  color: var(--near-black);
818  margin-bottom: 16px;
819}
820.cta-inner .gold-accent { color: var(--tpai-gold-dark); }
821.cta-desc {
822  font-size: 16px;
823  color: var(--dark-gray);
824  line-height: 1.7;
825  margin-bottom: 12px;
826}
827.cta-price {
828  font-size: 20px;
829  font-weight: 700;
830  color: var(--near-black);
831  margin-bottom: 32px;
832}
833.cta-price .gold { color: var(--tpai-gold-dark); }
834.cta-buttons {
835  display: flex;
836  justify-content: center;
837  gap: 16px;
838  flex-wrap: wrap;
839  margin-bottom: 24px;
840}
841.cta-note {
842  font-size: 14px;
843  color: var(--mid-gray);
844  line-height: 1.6;
845}
846
847/* ============================================================
848   CONTACT
849   ============================================================ */
850.contact {
851  background: var(--white);
852  padding: 64px 0;
853}
854.contact-inner {
855  max-width: 560px;
856  margin: 0 auto;
857  text-align: center;
858}
859.contact-inner h2 {
860  font-size: 20px;
861  font-weight: 700;
862  margin-bottom: 10px;
863  color: var(--near-black);
864}
865.contact-inner p {
866  font-size: 15px;
867  color: var(--dark-gray);
868  line-height: 1.7;
869}
870.contact-links {
871  display: flex;
872  justify-content: center;
873  gap: 28px;
874  margin-top: 16px;
875  flex-wrap: wrap;
876}
877.contact-links a {
878  color: var(--tpai-blue);
879  font-size: 15px;
880  font-weight: 600;
881}
882.contact-links a:hover { text-decoration: underline; }
883
884/* ============================================================
885   FOOTER
886   ============================================================ */
887.site-footer {
888  background: var(--tpai-navy);
889  padding: 48px 0 32px;
890  border-top: 1px solid var(--dark-border);
891}
892.footer-grid {
893  display: grid;
894  grid-template-columns: 2fr 1fr 1fr 1fr;
895  gap: 40px;
896  margin-bottom: 40px;
897}
898.footer-brand {
899  font-weight: 700;
900  font-size: 16px;
901  color: var(--dark-text-primary);
902  margin-bottom: 12px;
903}
904.footer-brand .g { color: var(--tpai-gold); }
905.footer-brand-desc {
906  font-weight: 400;
907  font-size: 14px;
908  color: var(--dark-text-muted);
909  line-height: 1.65;
910  max-width: 320px;
911}
912.footer-col-title {
913  font-weight: 500;
914  font-size: 11px;
915  letter-spacing: 0.2em;
916  text-transform: uppercase;
917  color: var(--dark-text-muted);
918  margin-bottom: 16px;
919}
920.footer-links {
921  list-style: none;
922  display: flex;
923  flex-direction: column;
924  gap: 10px;
925}
926.footer-links a {
927  font-weight: 400;
928  font-size: 14px;
929  color: var(--dark-text-secondary);
930  transition: color 0.2s ease;
931}
932.footer-links a:hover { color: var(--dark-text-primary); text-decoration: underline; }
933.footer-bio {
934  font-weight: 400;
935  font-size: 13px;
936  color: var(--dark-text-muted);
937  line-height: 1.6;
938  margin-top: 12px;
939  max-width: 260px;
940}
941.footer-bottom {
942  border-top: 1px solid var(--dark-border);
943  padding-top: 24px;
944  display: flex;
945  justify-content: space-between;
946  align-items: center;
947}
948.footer-copy {
949  font-weight: 400;
950  font-size: 13px;
951  color: var(--dark-text-muted);
952}
953.footer-legal {
954  display: flex;
955  gap: 20px;
956}
957.footer-legal a {
958  font-weight: 400;
959  font-size: 13px;
960  color: var(--dark-text-muted);
961  transition: color 0.2s ease;
962}
963.footer-legal a:hover {
964  color: var(--dark-text-primary);
965  text-decoration: underline;
966}
967
968/* ============================================================
969   RESPONSIVE
970   ============================================================ */
971@media (max-width: 768px) {
972  .nav-links { display: none; }
973  .nav-toggle { display: block; }
974
975  .site-nav.open .nav-links {
976    display: flex;
977    flex-direction: column;
978    position: absolute;
979    top: 64px;
980    left: 0; right: 0;
981    background: rgba(11, 18, 33, 0.97);
982    backdrop-filter: blur(12px);
983    padding: 24px;
984    gap: 20px;
985    border-bottom: 1px solid var(--dark-border);
986  }
987
988  .hero { padding: 120px 0 56px; }
989  .hero-grid { grid-template-columns: 1fr; gap: 40px; }
990  .hero-code { max-width: 100%; }
991  .hero-buttons { flex-direction: column; }
992  .hero-buttons .btn { width: 100%; max-width: 320px; }
993
994  .problem { padding: 56px 0; }
995  .versus-grid { grid-template-columns: 1fr; }
996
997  .how-it-works { padding: 56px 0; }
998  .how-grid { grid-template-columns: 1fr; }
999
1000  .drop-in { padding: 56px 0; }
1001  .drop-in-content { grid-template-columns: 1fr; }
1002
1003  .supported { padding: 56px 0; }
1004  .supported-grid { grid-template-columns: 1fr; }
1005
1006  .security { padding: 56px 0; }
1007
1008  .cta-section { padding: 56px 0; }
1009  .cta-buttons { flex-direction: column; align-items: center; }
1010  .cta-buttons .btn { width: 100%; max-width: 320px; }
1011
1012  .footer-grid { grid-template-columns: 1fr; gap: 32px; }
1013  .footer-bottom { flex-direction: column; gap: 8px; text-align: center; }
1014  .footer-legal { justify-content: center; gap: 16px; flex-wrap: wrap; }
1015
1016  pre { font-size: 11px; padding: 16px; }
1017}
1018
1019@media (max-width: 480px) {
1020  .tools-grid { grid-template-columns: 1fr; }
1021  .compare-table { font-size: 12px; }
1022  .compare-table thead th, .compare-table tbody td { padding: 10px 12px; }
1023}
1024</style>
1025</head>
1026<body>
1027
1028<!-- ============================================================
1029     NAV
1030     ============================================================ -->
1031<nav class="site-nav" id="siteNav" data-active="developer"></nav>
1032
1033<!-- ============================================================
1034     HERO
1035     ============================================================ -->
1036<section class="hero">
1037  <div class="wrap">
1038    <div class="hero-grid">
1039      <div class="hero-copy">
1040        <p class="hero-overline">Developer API</p>
1041        <h1 class="hero-headline">
1041Same API shape. <span class="gold">No exposure.</span></h1>
1042        <p class="hero-sub">OpenAI-compatible endpoint inside your VPN. Point any tool that speaks the OpenAI REST format at it. Your prompts never cross the public internet.</p>
1043        <div class="hero-buttons">
1044          <a href="https://buy.stripe.com/fZu4gA8Is3C68Gv7mC8ww04" class="btn btn--gold" target="_blank">Get API access &mdash; $199/mo</a>
1045          <a href="#how-it-works" class="btn btn--outline">How it works</a>
1046        </div>
1047      </div>
1048      <div class="hero-code">
1049        <div class="code-block">
1050          <div class="code-chrome">
1051            <span class="code-chrome-dot"></span>
1052            <span class="code-chrome-dot"></span>
1053            <span class="code-chrome-dot"></span>
1054            <span class="code-chrome-label">terminal</span>
1055          </div>
1056<pre><span class="comment"># Two environment variables. That's the migration.</span>
1057<span class="cmd">export</span> OPENAI_BASE_URL=<span class="string">"http://api.us-east-1.pro.tpai.internal/v1"</span>
1058<span class="cmd">export</span> OPENAI_API_KEY=<span class="string">"tpai-sk-..."</span>
1059
1060<span class="comment"># Your existing code works unchanged.</span>
1061<span class="cmd">curl</span> <span class="flag">-s</span> <span class="flag">-X</span> POST \
1062  <span class="flag">-H</span> <span class="string">"Authorization: Bearer $OPENAI_API_KEY"</span> \
1063  <span class="flag">-H</span> <span class="string">"Content-Type: application/json"</span> \
1064  <span class="flag">-d</span> <span class="string">'{"model":"anthropic.claude-3-5-haiku-20241022-v1:0",
1065       "messages":[{"role":"user","content":"Hello."}],
1066       "max_tokens":50}'</span> \
1067  <span class="url">"$OPENAI_BASE_URL/chat/completions"</span></pre>
1068        </div>
1069      </div>
1070    </div>
1071  </div>
1072</section>
1073
1074<!-- ============================================================
1075     THE PROBLEM
1076     ============================================================ -->
1077<section class="problem">
1078  <div class="wrap">
1079    <div class="problem-header">
1080      <p class="overline overline--blue">The tradeoff you should not have to make</p>
1081      <h2 class="section-headline">Every API call to a public provider <span class="gold-accent">crosses the open internet</span></h2>
1082      <p class="problem-sub">Your prompt leaves your network, lands on their servers, passes through their infrastructure. You are trusting their retention policy, their access controls, and their incident response.</p>
1083    </div>
1084
1085    <div class="versus-grid">
1086      <div class="versus-card versus-card--public">
1087        <span class="versus-tag versus-tag--public">Public LLM API</span>
1088        <h3>Your data on their servers</h3>
1089        <div class="versus-path">
1090          your machine <span class="red">&rarr;</span> public internet <span class="red">&rarr;</span> provider CDN <span class="red">&rarr;</span> provider SaaS fleet <span class="red">&rarr;</span> their model hosts
1091        </div>
1092        <ul class="versus-facts">
1093          <li>Prompts cross the public internet in every request</li>
1094          <li>Provider has technical ability to read your data</li>
1095          <li>Retention, logging, and training governed by their policy</li>
1096          <li>Credential compromise = internet-reachable endpoint</li>
1097          <li>Audit trail owned and controlled by the provider</li>
1098        </ul>
1099      </div>
1100
1101      <div class="versus-card versus-card--private">
1102        <span class="versus-tag versus-tag--private">TPAI API</span>
1103        <h3>Your data inside the boundary</h3>
1104        <div class="versus-path">
1105          your machine <span class="green">&rarr;</span> VPN tunnel <span class="green">&rarr;</span> private VPC <span class="green">&rarr;</span> Bedrock <span class="green">&rarr;</span> response back to you
1106        </div>
1107        <ul class="versus-facts">
1108          <li>No packet crosses the public internet</li>
1109          <li>No third-party SaaS endpoint in the traffic path</li>
1110          <li>Prompts and completions never leave the air-gapped environment</li>
1111          <li>VPN-only: endpoint has no public DNS or public IP</li>
1112          <li>
1112Audit trail stays inside the TPAI infrastructure</li>
1113        </ul>
1114      </div>
1115    </div>
1116  </div>
1117</section>
1118
1119<!-- ============================================================
1120     HOW IT WORKS
1121     ============================================================ -->
1122<section class="how-it-works" id="how-it-works">
1123  <div class="wrap">
1124    <div class="how-header">
1125      <p class="overline overline--gold">How it works</p>
1126      <h2 class="section-headline">OpenAI wire format. <span class="gold-accent">Air-gapped infrastructure.</span></h2>
1127      <p class="how-sub">The proxy sits behind an internal load balancer in your TPAI VPC. It speaks the OpenAI REST shape and delegates to AWS Bedrock via PrivateLink. No internet hop anywhere in the chain.</p>
1128    </div>
1129
1130    <div class="how-grid">
1131      <div class="how-card">
1132        <div class="how-card-icon">
1133          <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
1134        </div>
1135        <h4>VPN-only endpoint</h4>
1136        <p>No public DNS. No public IP. The only way to reach the proxy is through your encrypted VPN tunnel. If the tunnel is down, the endpoint does not exist.</p>
1137      </div>
1138      <div class="how-card">
1139        <div class="how-card-icon">
1140          <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0110 0v4"/><circle cx="12" cy="16" r="1"/></svg>
1141        </div>
1142        <h4>Per-user API keys</h4>
1143        <p>Every user gets their own <code style="font-family:var(--font-mono);font-size:12px;color:var(--tpai-gold);">tpai-sk-</code> key, stored in DynamoDB encrypted with a customer-managed KMS key. No shared tokens. No wildcard credentials.</p>
1144      </div>
1145      <div class="how-card">
1146        <div class="how-card-icon">
1147          <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10"/><line x1="4.93" y1="4.93" x2="19.07" y2="19.07"/></svg>
1148        </div>
1149        <h4>No third-party provider</h4>
1150        <p>Traffic terminates at AWS Bedrock via PrivateLink inside the TPAI infrastructure. No OpenAI. No Anthropic SaaS. No Google AI Studio. The models run inside the air-gapped boundary.</p>
1151      </div>
1152    </div>
1153
1154    <div class="traffic-path">
1155      <div class="code-block">
1156        <div class="code-chrome">
1157          <span class="code-chrome-dot"></span>
1158          <span class="code-chrome-dot"></span>
1159          <span class="code-chrome-dot"></span>
1160          <span class="code-chrome-label">traffic path</span>
1161        </div>
1162<pre><span class="comment"># Every hop after your laptop is inside the air-gapped VPC.</span>
1163<span class="comment"># No packet on this path crosses the public internet.</span>
1164
1165your laptop
1166  <span class="green">&rarr;</span> <span class="string">VPN tunnel</span> (encrypted)
1167    <span class="green">&rarr;</span> <span class="string">internal ALB</span> (private IP only)
1168      <span class="green">&rarr;</span> <span class="string">proxy Lambda</span> (validates your tpai-sk- key)
1169        <span class="green">&rarr;</span> <span class="string">Bedrock Gateway ALB</span> (SG-restricted)
1170          <span class="green">&rarr;</span> <span class="string">AWS Bedrock</span> (TPAI-managed, via PrivateLink)
1171            <span class="green">&rarr;</span> response back to you</pre>
1172      </div>
1173    </div>
1174  </div>
1175</section>
1176
1177<!-- ============================================================
1178     DROP-IN COMPATIBLE
1179     ============================================================ -->
1180<section class="drop-in">
1181  <div class="wrap">
1182    <div class="drop-in-header">
1183      <p class="overline overline--blue">Drop-in compatible</p>
1184      <h2 class="section-headline">Change two env vars. <span class="gold-accent">Keep your stack.</span></h2>
1185      <p class="drop-in-sub">If your tool speaks the OpenAI REST format, it works with the TPAI API. No new SDK. No wrapper library. No code changes.</p>
1186    </div>
1187
1188    <div class="drop-in-content">
1189      <div>
1190        <div class="code-block">
1191          <div class="code-chrome">
1192            <span class="code-chrome-dot"></span>
1193            <span class="code-chrome-dot"></span>
1194            <span class="code-chrome-dot"></span>
1195            <span class="code-chrome-label">python</span>
1196          </div>
1197<pre><span class="kw">from</span> openai <span class="kw">import</span> OpenAI
1198
1199client <span class="op">=</span> OpenAI(
1200    base_url<span class="op">=</span><span class="string">"http://api.us-east-1.pro.tpai.internal/v1"</span>,
1201    api_key<span class="op">=</span><span class="string">"tpai-sk-..."</span>
1202)
1203
1204response <span class="op">=</span> client.chat.completions.<span class="fn">create</span>(
1205    model<span class="op">=</span><span class="string">"anthropic.claude-3-5-haiku-20241022-v1:0"</span>,
1206    messages<span class="op">=</span>[{<span class="key">"role"</span>: <span class="string">"user"</span>,
1207               <span class="key">"content"</span>: <span class="string">"Summarize this contract."</span>}],
1208    max_tokens<span class="op">=</span><span class="num">500</span>
1209)
1210
1211<span class="fn">print</span>(response.choices[<span class="num">0</span>].message.content)</pre>
1212        </div>
1213      </div>
1214
1215      <div>
1216        <h3 style="font-size:18px;font-weight:700;margin-bottom:20px;color:var(--near-black);">Works with your tools</h3>
1217        <div class="tools-grid">
1218          <div class="tool-card">
1219            <div class="tool-card-name">Cline</div>
1220            <div class="tool-card-desc">VS Code AI agent</div>
1221          </div>
1222          <div class="tool-card">
1223            <div class="tool-card-name">n8n</div>
1224            <div class="tool-card-desc">Workflow automation</div>
1225          </div>
1226          <div class="tool-card">
1227            <div class="tool-card-name">Open Code</div>
1228            <div class="tool-card-desc">Terminal coding agent</div>
1229          </div>
1230          <div class="tool-card">
1231            <div class="tool-card-name">Open Notebook</div>
1232            <div class="tool-card-desc">AI notebook interface</div>
1233          </div>
1234          <div class="tool-card">
1235            <div class="tool-card-name">openai SDK</div>
1236            <div class="tool-card-desc">Python &amp; JS/TS</div>
1237          </div>
1238          <div class="tool-card">
1239            <div class="tool-card-name">curl</div>
1240            <div class="tool-card-desc">Direct HTTP</div>
1241          </div>
1242          <div class="tool-card tool-card--any">
1243            <div class="tool-card-name">Any OpenAI-compatible client</div>
1244            <div class="tool-card-desc">If it can point at a custom base URL, it works.</div>
1245          </div>
1246        </div>
1247      </div>
1248    </div>
1249  </div>
1250</section>
1251
1252<!-- ============================================================
1253     WHAT'S SUPPORTED
1254     ============================================================ -->
1255<section class="supported">
1256  <div class="wrap">
1257    <div class="supported-header">
1258      <p class="overline overline--blue">What's available</p>
1259      <h2 class="section-headline">Routes, models, <span class="gold-accent">and honest limits</span></h2>
1260    </div>
1261
1262    <div class="supported-grid">
1263      <div class="supported-card">
1264        <h4>Supported routes</h4>
1265        <p><code>POST /v1/chat/completions</code> with optional tool calls and function calling.</p>
1266        <p style="margin-top:8px;"><code>GET /v1/models</code> returns the live list of available Bedrock models.</p>
1267        <p style="margin-top:8px;"><code>GET /health</code> unauthenticated liveness probe.</p>
1268      </div>
1269      <div class="supported-card">
1270        <h4>Available models</h4>
1271        <p>Claude 3.5 Haiku, Claude 3.5 Sonnet, and any other Claude-family model your TPAI account has Bedrock access to. Model list is live from <code>/v1/models</code>.</p>
1272      </div>
1273      <div class="supported-card">
1274        <h4>What's not here yet</h4>
1275        <p>Embeddings, image generation, audio, moderation. Streaming is accepted (<code>stream: true</code>) but delivered as a single SSE chunk. Clients parse this correctly; you just won't see tokens arrive incrementally.</p>
1276      </div>
1277    </div>
1278
1279    <div class="supported-note">
1280      <p><strong>Tool calls and function calling work</strong> if the underlying model supports them. The proxy is pure pass-through: whatever JSON your client sends goes straight to Bedrock, and whatever comes back goes straight to your client.</p>
1281    </div>
1282  </div>
1283</section>
1284
1285<!-- ============================================================
1286     SECURITY COMPARISON
1287     ============================================================ -->
1288<section class="security">
1289  <div class="wrap">
1290    <div class="security-header">
1291      <p class="overline overline--gold">Security posture</p>
1292      <h2 class="section-headline">The comparison your <span class="gold-accent">compliance team wants to see</span></h2>
1293    </div>
1294
1295    <div class="compare-table-wrap">
1296      <table class="compare-table">
1297        <thead>
1298          <tr>
1299            <th>Dimension</th>
1300            <th class="col-tpai">TPAI API</th>
1301            <th class="col-public">Public LLM API</th>
1302          </tr>
1303        </thead>
1304        <tbody>
1305          <tr>
1306            <td>Network path</td>
1307            <td class="highlight">VPN &rarr; air-gapped VPC &rarr; Bedrock via PrivateLink</td>
1308            <td class="dim">Public internet &rarr; provider SaaS &rarr; their hosts</td>
1309          </tr>
1310          <tr>
1311            <td>Endpoint reachability</td>
1312            <td class="highlight">Private DNS, private IP, VPN-only</td>
1313            <td class="dim">Public DNS, public IP, reachable from anywhere</td>
1314          </tr>
1315          <tr>
1316            <td>Credential scope</td>
1317            <td class="highlight">Per-user key, validated inside the TPAI boundary</td>
1318            <td class="dim">Per-account or per-project key, validated by provider</td>
1319          </tr>
1320          <tr>
1321            <td>Key storage</td>
1322            <td class="highlight">CMK-encrypted DynamoDB, air-gapped VPC</td>
1323            <td class="dim">Provider's key vault, provider operators can read</td>
1324          </tr>
1325          <tr>
1326            <td>
1326Data residency</td>
1327            <td class="highlight">Never leaves the air-gapped TPAI environment</td>
1328            <td class="dim">Leaves your network to provider infrastructure</td>
1329          </tr>
1330          <tr>
1331            <td>Provider access to prompts</td>
1332            <td class="highlight">None. No third-party SaaS in the traffic path.</td>
1333            <td class="dim">Provider has technical ability per their ToS</td>
1334          </tr>
1335          <tr>
1336            <td>Audit trail</td>
1337            <td class="highlight">CloudWatch + CloudTrail inside the boundary</td>
1338            <td class="dim">Provider's dashboard, provider's retention</td>
1339          </tr>
1340          <tr>
1341            <td>Internet egress</td>
1342            <td class="highlight">None. VPC has no NAT to public internet.</td>
1343            <td class="dim">Required for every request</td>
1344          </tr>
1345        </tbody>
1346      </table>
1347    </div>
1348  </div>
1349</section>
1350
1351<!-- ============================================================
1352     CTA
1353     ============================================================ -->
1354<section class="cta-section" id="get-started">
1355  <div class="wrap">
1356    <div class="cta-inner">
1357      <h2 class="section-headline">API access included with <span class="gold-accent">Pro</span></h2>
1358      <p class="cta-desc">Sign up for Totally Private AI Pro and you get the chat interface and the developer API. One subscription, both surfaces. Connect to VPN, get your key, start building.</p>
1359      <p class="cta-price"><span class="gold">$199/month</span> &mdash; founding beta pricing, locked while you remain subscribed.</p>
1360      <div class="cta-buttons">
1361        <a href="https://buy.stripe.com/fZu4gA8Is3C68Gv7mC8ww04" class="btn btn--gold" target="_blank">Get API access now</a>
1362        <a href="/pro" class="btn btn--outline-blue">See full Pro details</a>
1363      </div>
1364      <p class="cta-note">You will receive a provisioning package immediately. VPN credentials, API key generation, and full docs included. Up and running in minutes.</p>
1365    </div>
1366  </div>
1367</section>
1368
1369<!-- ============================================================
1370     CONTACT
1371     ============================================================ -->
1372<section class="contact">
1373  <div class="wrap">
1374    <div class="contact-inner">
1375      <h2>Questions about the API?</h2>
1376      <p>Architecture deep-dive, integration help, or security review. We are happy to walk through the details.</p>
1377      <div class="contact-links">
1378        <a href="mailto:[email protected]">[email protected]</a>
1379        <a href="https://calendly.com/rcastillo-totally-private" target="_blank">Book a call</a>
1380      </div>
1381    </div>
1382  </div>
1383</section>
1384
1385<!-- ============================================================
1386     FOOTER
1387     ============================================================ -->
1388<footer class="site-footer" id="siteFooter"></footer>
1389
1390<script>
1391</script>
1391
1392
1393<script src="/js/site-chrome.js"></script>
1393
1394</body>
1395</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.