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 — $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">→</span> public internet <span class="red">→</span> provider CDN <span class="red">→</span> provider SaaS fleet <span class="red">→</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">→</span> VPN tunnel <span class="green">→</span> private VPC <span class="green">→</span> Bedrock <span class="green">→</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">→</span> <span class="string">VPN tunnel</span> (encrypted) 1167 <span class="green">→</span> <span class="string">internal ALB</span> (private IP only) 1168 <span class="green">→</span> <span class="string">proxy Lambda</span> (validates your tpai-sk- key) 1169 <span class="green">→</span> <span class="string">Bedrock Gateway ALB</span> (SG-restricted) 1170 <span class="green">→</span> <span class="string">AWS Bedrock</span> (TPAI-managed, via PrivateLink) 1171 <span class="green">→</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 & 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 → air-gapped VPC → Bedrock via PrivateLink</td> 1308 <td class="dim">Public internet → provider SaaS → 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> — 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.