PageSourceSearch

https://foundrynet.io/docs

html foundrynet.io collected 2026-10-03 21:12:29 UTC 58,732 bytes, 1,092 lines download raw bytes

1<!doctype html>
2<html lang="en">
3<head>
4<!-- Privacy-friendly analytics by Plausible -->
5<script async src="https://plausible.io/js/pa-QdPlluyoOE1jovqrkv-ed.js"></script>
vendor: 1 bytes, line 5
5
6<script>
7  window.plausible=window.plausible||function(){(plausible.q=plausible.q||[]).push(arguments)},plausible.init=plausible.init||function(i){plausible.o=i||{}};
8  plausible.init()
9</script>
9
10<meta charset="utf-8">
11<meta name="viewport" content="width=device-width,initial-scale=1">
12<meta name="google-site-verification" content="-Vk03AgXINQMCfsTNJqtCnbcdC92p3WtyyDDOCnckAU" />
13<title>FoundryNet, Developer Docs & API Reference</title>
14<link rel="canonical" href="https://foundrynet.io/docs">
15<meta name="description" content="FoundryNet Forge API reference: /v1/* endpoints, the 32-tool MCP server, quick start, reliability and security internals.">
16<meta property="og:title" content="FoundryNet, Developer Docs & API Reference">
17<meta property="og:description" content="FoundryNet Forge API reference: /v1/* endpoints, the 32-tool MCP server, quick start, reliability and security internals.">
18<link rel="preconnect" href="https://fonts.googleapis.com">
19<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
20<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
21<style>
22  :root {
23    --bg:        #08080d;
24    --bg-elev:   #0c0f17;
25    --bg-term:   #000000;
26    --bg-code:   #050609;
27    --rule:      #16202e;
28    --rule-soft: #0f1722;
29    --fg:        #d8dfeb;
30    --fg-mid:    #8d99ad;
31    --fg-dim:    #5a6678;
32    --jade:      #1a9058;
33    --jade-soft: #2dbb78;
34    --jade-glow: rgba(45, 187, 120, 0.35);
35    --amber:     #f0a030;
36    --amber-glow: rgba(240, 160, 48, 0.35);
37    --max:       1020px;
38  }
39  * { box-sizing: border-box; }
40  html, body { margin: 0; padding: 0; background: var(--bg); color: var(--fg); }
41  body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
42         font-size: 15px; line-height: 1.7; font-weight: 400;
43         -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
44  h1, h2, h3 { font-family: 'JetBrains Mono', ui-monospace, monospace;
45               font-weight: 500; margin: 0; letter-spacing: -0.01em; }
46  h1 { font-size: clamp(36px, 5.5vw, 60px); line-height: 1.05;
47       color: var(--fg); font-weight: 600; letter-spacing: -0.025em; }
48  h2 { font-size: clamp(22px, 2.6vw, 30px); color: var(--fg);
49       margin-bottom: 18px; font-weight: 500; letter-spacing: -0.01em; }
50  h3 { font-size: 11px; color: var(--jade-soft); margin-bottom: 10px;
51       font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; }
52  p  { margin: 0 0 14px; color: var(--fg); }
53  a  { color: var(--jade-soft); text-decoration: none;
54       transition: color .15s ease; }
55  a:hover { color: var(--amber); }
56  ul, ol { color: var(--fg-mid); padding-left: 20px; line-height: 1.85; }
57  li { margin: 4px 0; }
58  code { font-family: 'JetBrains Mono', monospace; font-size: 0.92em;
59         color: var(--amber); background: transparent; }
60
61  section { padding: 90px 28px; max-width: var(--max); margin: 0 auto; }
62  section.first { padding-top: 130px; }
63
64  .label { font-family: 'JetBrains Mono', monospace; font-size: 11px;
65           color: var(--fg-dim); font-weight: 400; letter-spacing: 0.22em;
66           text-transform: uppercase; }
67
68  /* HERO ---------------------------------------------------------- */
69  .hero p.sub { font-size: 22px; color: var(--fg-mid); margin: 22px 0 32px;
70                max-width: 640px; font-weight: 300; line-height: 1.5; }
71  .proof { color: var(--jade-soft); font-size: 12px; letter-spacing: 0.18em;
72           text-transform: uppercase; margin-bottom: 44px; font-weight: 400;
73           font-family: 'JetBrains Mono', monospace; }
74  .proof .dot { color: var(--rule); margin: 0 12px; }
75  .ctas { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 64px; }
76  .btn { display: inline-block; padding: 13px 24px; border-radius: 4px;
77         font-family: 'JetBrains Mono', monospace; font-size: 13px;
78         font-weight: 500; letter-spacing: 0.02em;
79         transition: all .18s ease; cursor: pointer; }
80  .btn.primary { background: var(--jade); color: #050609;
81                 box-shadow: 0 0 0 1px var(--jade), 0 4px 24px rgba(26, 144, 88, 0.18); }
82  .btn.primary:hover { background: var(--jade-soft); color: #050609;
83                       box-shadow: 0 0 0 1px var(--jade-soft), 0 6px 32px rgba(45, 187, 120, 0.32); }
84  .btn.secondary { background: transparent; color: var(--fg);
85                   border: 1px solid var(--rule); }
86  .btn.secondary:hover { border-color: var(--jade-soft); color: var(--jade-soft); }
87
88  /* SIGNUP PANEL --------------------------------------------------- */
89  .signup { display: none; margin: 0 0 64px; padding: 24px;
90            border: 1px solid var(--rule); border-left: 2px solid var(--jade);
91            background: var(--bg-elev); border-radius: 4px;
92            max-width: 640px; }
93  .signup.open { display: block; }
94  .signup .row { display: flex; flex-wrap: wrap; gap: 10px;
95                 margin-top: 8px; }
96  .signup label { font-family: 'JetBrains Mono', monospace; font-size: 11px;
97                  color: var(--fg-dim); letter-spacing: 0.18em;
98                  text-transform: uppercase; }
99  .signup input[type=email] { flex: 1 1 280px; padding: 12px 14px;
100                              background: var(--bg-code); color: var(--fg);
101                              font-family: 'JetBrains Mono', monospace;
102                              font-size: 13px; border: 1px solid var(--rule);
103                              border-radius: 4px; outline: none;
104                              transition: border-color .15s; }
105  .signup input[type=email]:focus { border-color: var(--jade-soft); }
106  .signup .help { color: var(--fg-dim); font-size: 12px; margin-top: 10px; }
107  .signup-options { display: grid; grid-template-columns: 1fr 1fr;
108                    gap: 14px; }
109  .signup-options > div { background: var(--bg-code);
110                          border: 1px solid var(--rule); border-radius: 5px;
111                          padding: 18px; display: flex; flex-direction: column;
112                          gap: 10px; }
113  .signup-options .free-option { border-left: 2px solid var(--jade); }
114  .signup-options .paid-option { border-left: 2px solid var(--amber); }
115  .signup-options h3 { font-family: 'JetBrains Mono', monospace;
116                       font-size: 13px; color: var(--fg);
117                       margin: 0; font-weight: 500; letter-spacing: 0.02em; }
118  .signup-options p  { color: var(--fg-mid); font-size: 12.5px;
119                       margin: 0 0 4px; line-height: 1.5; }
120  .signup-options input[type=email] { width: 100%; padding: 10px 12px;
121                                      background: var(--bg); color: var(--fg);
122                                      border: 1px solid var(--rule);
123                                      border-radius: 4px;
124                                      font-family: 'JetBrains Mono', monospace;
125                                      font-size: 12px; outline: none; }
126  .signup-options input[type=email]:focus { border-color: var(--jade-soft); }
127  .signup-options button { width: 100%; }
128  @media (max-width: 600px) { .signup-options { grid-template-columns: 1fr; } }
129  .signup .err  { color: var(--amber); font-size: 12px; margin-top: 10px;
130                  display: none; font-family: 'JetBrains Mono', monospace; }
131  .signup .err.show { display: block; }
132  .signup .keybox { margin-top: 10px; padding: 14px; background: var(--bg-code);
133                    border: 1px dashed var(--jade); border-radius: 4px;
134                    color: var(--amber); font-family: 'JetBrains Mono', monospace;
135                    font-size: 13px; word-break: break-all; line-height: 1.5; }
136  .signup .copy { margin-top: 10px; }
137  .signup .ok-line { color: var(--jade-soft); font-family: 'JetBrains Mono', monospace;
138                     font-size: 12px; letter-spacing: 0.06em; margin-top: 14px; }
139  .signup h3 { margin-bottom: 8px; }
140
141  /* TERMINAL MOCKUP ----------------------------------------------- */
142  .term { background: var(--bg-term); border: 1px solid var(--rule);
143          border-radius: 6px; padding: 26px 28px;
144          font-family: 'JetBrains Mono', monospace; font-size: 13.5px;
145          line-height: 1.95; max-width: 740px;
146          box-shadow: 0 24px 80px rgba(0,0,0,.6),
147                      0 0 0 1px rgba(45, 187, 120, 0.04); }
148  .term .you, .term .feed { color: var(--jade-soft);
149                            text-shadow: 0 0 10px var(--jade-glow); }
150  .term .you::before, .term .feed::before { content: "> "; color: var(--jade);
151                                            text-shadow: 0 0 10px var(--jade-glow); }
152  .term .ok   { color: var(--jade-soft); opacity: 0.95; }
153  .term .ok::before { content: "✓ "; color: var(--jade); }
154  .term .fire { color: var(--amber); font-weight: 500;
155                text-shadow: 0 0 12px var(--amber-glow); }
156  .term .fire::before { content: "🔥 "; }
157  .term .gap { display: block; height: 12px; }
158
159  /* BEYOND MACHINES (operations pitch) ---------------------------- */
160  .beyond { display: grid; grid-template-columns: 1fr 1fr; gap: 36px;
161            margin-top: 40px; }
162  .beyond .col h3 { font-family: 'JetBrains Mono', monospace;
163                    color: var(--jade-soft); font-size: 12px;
164                    font-weight: 500; letter-spacing: 0.18em;
165                    text-transform: uppercase; margin-bottom: 14px; }
166  .beyond .col p { color: var(--fg-mid); font-size: 14.5px;
167                   line-height: 1.65; margin: 0 0 18px;
168                   font-weight: 400; }
169  .term.compact { padding: 18px 20px; font-size: 12.5px;
170                  line-height: 1.85; max-width: none; box-shadow: none;
171                  border: 1px solid var(--rule); }
172  .beyond-foot { margin-top: 32px; color: var(--fg-mid);
173                 font-size: 14px; max-width: 720px;
174                 font-style: normal; }
175  .beyond-foot strong { color: var(--fg); font-weight: 500; }
176  .beyond-sub { color: var(--fg-mid); font-size: 16px; line-height: 1.55;
177                margin: 6px 0 0; max-width: 760px; font-weight: 300; }
178  @media (max-width: 720px) {
179    .beyond { grid-template-columns: 1fr; }
180  }
181
182  /* HOW IT WORKS -------------------------------------------------- */
183  .grid3 { display: grid; gap: 36px;
184           grid-template-columns: repeat(3, 1fr); margin-top: 40px; }
185  .col h3 { display: flex; align-items: baseline; gap: 12px; }
186  .col h3 .num { color: var(--amber); font-size: 12px; letter-spacing: 0.1em; }
187  .col p { color: var(--fg-mid); font-size: 14.5px; line-height: 1.65;
188           margin: 0; font-weight: 400; }
189
190  /* CONNECTIONS --------------------------------------------------- */
191  .pillrow { display: flex; flex-wrap: wrap; align-items: center;
192             gap: 8px 8px; margin-bottom: 22px; }
193  .pillrow .label { width: 100%; margin-bottom: 8px; }
194  .pill { font-family: 'JetBrains Mono', monospace; font-size: 12.5px;
195          color: var(--fg-mid); background: transparent;
196          border: 1px solid var(--rule); border-radius: 3px;
197          padding: 7px 13px; font-weight: 400;
198          transition: all .15s ease; }
199  .pill:hover { color: var(--jade-soft); border-color: rgba(45,187,120,0.35); }
200  .footnote { color: var(--fg-dim); font-size: 13px; line-height: 1.7;
201              margin-top: 24px; max-width: 720px; font-weight: 400; }
202
203  /* PRICING (outlined cards) -------------------------------------- */
204  .grid4 { display: grid; gap: 14px;
205           grid-template-columns: repeat(4, 1fr); margin: 32px 0 28px; }
206  .card { background: transparent;
207          border: 1px solid var(--rule);
208          border-top: 1px solid rgba(26, 144, 88, 0.45);
209          border-radius: 4px; padding: 26px 22px;
210          transition: border-color .2s ease, transform .2s ease; }
211  .card:hover { transform: translateY(-2px); border-top-color: var(--jade-soft); }
212  .card .name  { color: var(--fg-mid); font-size: 11px; font-weight: 400;
213                 letter-spacing: 0.18em; text-transform: uppercase;
214                 margin-bottom: 14px; font-family: 'JetBrains Mono', monospace; }
215  .card .price { color: var(--fg); font-size: 28px; font-weight: 500;
216                 margin-bottom: 12px; font-family: 'JetBrains Mono', monospace;
217                 letter-spacing: -0.02em; }
218  .card .price .unit { color: var(--fg-dim); font-size: 13px; font-weight: 400;
219                       letter-spacing: 0; margin-left: 2px; }
220  .card .desc  { color: var(--fg-mid); font-size: 13px; line-height: 1.55;
221                 font-weight: 400; }
222  .free-line { color: var(--fg-mid); font-size: 13px; line-height: 1.85;
223               font-weight: 400; max-width: 680px; margin-top: 22px; }
224  .free-line strong { color: var(--fg); font-family: 'JetBrains Mono', monospace;
225                      font-weight: 500; }
226
227  /* PRICING TIERS ------------------------------------------------- */
228  .tier-grid { display: grid; gap: 16px; margin: 32px 0 12px;
229               grid-template-columns: repeat(3, 1fr); }
230  .tier { background: var(--bg-elev); border: 1px solid var(--rule);
231          border-top: 1px solid var(--rule);
232          border-radius: 5px; padding: 26px 24px;
233          display: flex; flex-direction: column; gap: 16px;
234          transition: border-color .2s ease, transform .2s ease; }
235  .tier:hover { transform: translateY(-2px); }
236  .tier-starter   { border-top: 2px solid var(--jade);
237                    box-shadow: 0 0 0 1px rgba(26,144,88,.18); }
238  .tier-growth    { border-top: 1px solid rgba(45,187,120,.45); }
239  .tier-enterprise{ border-top: 1px solid rgba(240,160,48,.45); }
240  .tier-head { display: flex; justify-content: space-between; align-items: baseline; }
241  .tier .name { font-family: 'JetBrains Mono', monospace; font-size: 11px;
242                color: var(--jade-soft); letter-spacing: 0.22em;
243                text-transform: uppercase; font-weight: 500; }
244  .tier-enterprise .name { color: var(--amber); }
245  .tier .badge { font-family: 'JetBrains Mono', monospace; font-size: 10px;
246                 color: var(--fg-dim); letter-spacing: 0.06em; }
247  .tier .badge.recommended { color: var(--jade-soft); }
248  .tier .price-list { display: flex; flex-direction: column; gap: 6px; }
249  .tier .line { display: flex; justify-content: space-between; align-items: baseline;
250                font-family: 'JetBrains Mono', monospace; font-size: 13px;
251                color: var(--fg-mid); padding: 4px 0;
252                border-bottom: 1px dashed var(--rule-soft); }
253  .tier .line:last-child { border-bottom: none; }
254  .tier .line .what { color: var(--fg-mid); }
255  .tier .line .amt  { color: var(--fg); font-weight: 500; }
256  .tier-growth .line .amt,
257  .tier-enterprise .line .amt { color: var(--jade-soft); }
258  .tier .desc { color: var(--fg-mid); font-size: 13px; line-height: 1.55;
259                margin: 0; }
260  .tier input[type=email] { width: 100%; padding: 10px 12px;
261                            background: var(--bg-code); color: var(--fg);
262                            border: 1px solid var(--rule); border-radius: 4px;
263                            font-family: 'JetBrains Mono', monospace;
264                            font-size: 12px; outline: none; box-sizing: border-box; }
265  .tier input[type=email]:focus { border-color: var(--jade-soft); }
266  .tier .cta { display: inline-block; padding: 11px 16px; border-radius: 4px;
267               font-family: 'JetBrains Mono', monospace; font-size: 12px;
268               font-weight: 500; letter-spacing: 0.04em; text-align: center;
269               transition: all .15s ease; cursor: pointer; border: none; }
270  .tier-starter .cta { background: var(--jade); color: #050609;
271                       box-shadow: 0 0 0 1px var(--jade), 0 4px 18px rgba(26,144,88,.18); }
272  .tier-starter .cta:hover { background: var(--jade-soft); color: #050609; }
273  .tier-growth .cta, .tier-enterprise .cta {
274    background: transparent; color: var(--fg); border: 1px solid var(--rule); }
275  .tier-growth .cta:hover, .tier-enterprise .cta:hover {
276    border-color: var(--jade-soft); color: var(--jade-soft); }
277  .tier .hint { color: var(--fg-dim); font-size: 11px; margin: 0;
278                font-family: 'JetBrains Mono', monospace; letter-spacing: 0.04em; }
279  @media (max-width: 760px) { .tier-grid { grid-template-columns: 1fr; } }
280
281  /* INTEGRATORS --------------------------------------------------- */
282  .integrators p { color: var(--fg-mid); max-width: 700px;
283                   margin-bottom: 28px; font-size: 15.5px; font-weight: 400;
284                   line-height: 1.7; }
285  .small-note { color: var(--fg-dim); font-size: 12.5px;
286                margin-top: 18px; font-style: italic; font-weight: 400; }
287
288  /* MCP / CODE ---------------------------------------------------- */
289  pre.code, pre { background: var(--bg-term); border: 1px solid var(--rule);
290                  border-radius: 6px; padding: 18px 22px;
291                  font-family: 'JetBrains Mono', monospace; font-size: 12.5px;
292                  color: var(--fg); overflow-x: auto; line-height: 1.55;
293                  margin: 12px 0; max-width: 740px;
294                  box-shadow: 0 16px 48px rgba(0,0,0,.4); }
295  pre code { color: var(--fg); font-size: inherit; }
296  pre.code .key { color: var(--amber); }
297  pre.code .str { color: var(--jade-soft); }
298  .mcp-summary { color: var(--fg-mid); font-size: 14px; margin-bottom: 22px;
299                 font-weight: 400; }
300  .mcp-links a { margin-right: 24px; font-family: 'JetBrains Mono', monospace;
301                 font-size: 13px; }
302
303  /* DOCS SECTION (nested below landing) --------------------------- */
304  .docs-divider { border: 0; border-top: 1px solid var(--rule);
305                  max-width: var(--max); margin: 60px auto 0; }
306  .docs-intro { color: var(--fg-mid); font-size: 14.5px;
307                margin-bottom: 32px; max-width: 720px; }
308  .docs h2 { margin-top: 56px; margin-bottom: 14px; }
309  .docs h2:first-of-type { margin-top: 0; }
310  .docs h3 { color: var(--jade-soft); margin-top: 30px; margin-bottom: 8px; }
311  .docs p { color: var(--fg-mid); font-size: 14px; line-height: 1.7; }
312  .docs ol, .docs ul { color: var(--fg-mid); font-size: 14px; }
313
314  /* LEGAL SECTION (TOS + Privacy, anchored at #terms / #privacy) -- */
315  .legal h1 { font-family: 'JetBrains Mono', monospace;
316              color: var(--jade-soft); font-size: 24px;
317              font-weight: 500; margin: 0 0 8px;
318              letter-spacing: -0.01em; }
319  .legal h2 { color: var(--amber); font-size: 16px;
320              margin: 40px 0 12px; font-family: 'JetBrains Mono', monospace; }
321  .legal h2:first-of-type { margin-top: 24px; }
322  .legal h3 { color: var(--fg); font-size: 14px;
323              margin: 24px 0 8px; letter-spacing: 0;
324              text-transform: none; font-family: 'JetBrains Mono', monospace; }
325  .legal p  { color: var(--fg-mid); font-size: 14px; line-height: 1.8;
326              margin: 0 0 14px; }
327  .legal .updated { color: var(--fg-dim); font-size: 12px;
328                    margin-bottom: 32px;
329                    font-family: 'JetBrains Mono', monospace;
330                    letter-spacing: 0.06em; }
331  .legal hr { border: none; border-top: 1px solid var(--rule);
332              margin: 48px 0; }
333  .endpoint { border-top: 1px solid var(--rule); padding-top: 20px;
334              margin-top: 26px; }
335  .endpoint:first-of-type { margin-top: 18px; }
336  .endpoint .method { display: inline-block; color: var(--bg);
337                      background: var(--jade); padding: 1px 9px;
338                      border-radius: 3px; font-weight: 600; font-size: 11px;
339                      vertical-align: 2px; font-family: 'JetBrains Mono', monospace;
340                      letter-spacing: 0.05em; }
341  .endpoint .method.get { background: var(--amber); }
342  .endpoint .path { color: var(--fg); font-size: 14px; }
343  .endpoint .desc { color: var(--fg-mid); margin: 6px 0 12px;
344                    font-size: 14px; line-height: 1.65; }
345
346  /* FOOTER (single line) ----------------------------------------- */
347  footer { border-top: 1px solid var(--rule); padding: 28px;
348           max-width: var(--max); margin: 80px auto 0;
349           color: var(--fg-dim); font-size: 12px; text-align: center;
350           font-family: 'JetBrains Mono', monospace; font-weight: 400;
351           letter-spacing: 0.02em; line-height: 1.8; }
352  footer .sep { color: var(--rule); margin: 0 12px; }
353  footer a { color: var(--fg-mid); }
354  footer a:hover { color: var(--jade-soft); }
355
356  /* RESPONSIVE --------------------------------------------------- */
357  @media (max-width: 760px) {
358    section { padding: 70px 22px; }
359    section.first { padding-top: 90px; }
360    .grid3 { grid-template-columns: 1fr; gap: 32px; }
361    .grid4 { grid-template-columns: 1fr 1fr; }
362    .term { font-size: 12px; padding: 20px; }
363    .hero p.sub { font-size: 18px; }
364    footer { font-size: 11px; }
365    footer .sep { margin: 0 8px; }
366  }
367  @media (max-width: 460px) {
368    .grid4 { grid-template-columns: 1fr; }
369    footer .sep.collapse { display: block; visibility: hidden; height: 4px; }
370  }
371  /* FN-NAV-CSS-OPEN */
372
373  /* TOP NAV ─────────────────────────────────────────────── */
374  .topnav { position:sticky; top:0; z-index:8000;
375            background:rgba(8,8,13,0.92); backdrop-filter:blur(10px);
376            -webkit-backdrop-filter:blur(10px);
377            border-bottom:1px solid var(--rule-soft);
378            padding:12px 28px; display:flex; align-items:center;
379            gap:32px; }
380  .topnav-brand { font-family:'JetBrains Mono',monospace;
381                  font-size:13px; font-weight:600; letter-spacing:0.02em;
382                  color:var(--fg); text-decoration:none; }
383  .topnav-brand:hover { color:var(--jade-soft); }
384  .topnav-dropdown { position:relative; }
385  .topnav-dropdown summary { font-family:'JetBrains Mono',monospace;
386                             font-size:12px; color:var(--fg-mid);
387                             letter-spacing:0.04em; cursor:pointer;
388                             list-style:none; padding:6px 10px;
389                             border-radius:3px; user-select:none;
390                             transition:color .15s ease; }
391  .topnav-dropdown summary::-webkit-details-marker { display:none; }
392  .topnav-dropdown summary:hover { color:var(--jade-soft); }
393  .topnav-dropdown summary::after { content:" ▾"; font-size:9px;
394                                    color:var(--fg-dim); margin-left:3px; }
395  .topnav-dropdown[open] summary { color:var(--jade-soft); }
396  .topnav-menu { position:absolute; top:calc(100% + 6px); left:0;
397                 min-width:190px; background:var(--bg-elev);
398                 border:1px solid var(--rule); border-radius:4px;
399                 padding:6px; box-shadow:0 8px 24px rgba(0,0,0,0.45);
400                 display:flex; flex-direction:column; gap:2px; }
401  .topnav-menu a { font-family:'JetBrains Mono',monospace; font-size:12px;
402                   color:var(--fg-mid); padding:8px 12px;
403                   text-decoration:none; border-radius:3px;
404                   transition:all .12s ease; }
405  .topnav-menu a:hover { background:var(--bg-code); color:var(--jade-soft); }
406  .topnav-menu a.active { color:var(--jade-soft);
407                          border-left:2px solid var(--jade-soft); padding-left:10px; }
408  .topnav-spacer { flex:1; }
409  .topnav-cta { font-family:'JetBrains Mono',monospace; font-size:12px;
410                color:var(--jade-soft); text-decoration:none;
411                letter-spacing:0.04em; padding:6px 10px;
412                transition:color .15s ease; }
413  .topnav-cta:hover { color:var(--amber); }
414  @media (max-width:640px) {
415    .topnav { padding:10px 14px; gap:8px 12px; flex-wrap:wrap; }
416    .topnav-spacer { flex-basis:100%; height:0; }
417    .topnav-cta { display:inline-block; font-size:12px; padding:4px 7px; }
418  }
419
420  /* FN-NAV-CSS-CLOSE */
421
422  /* per-machine cost calculator (added 2026-05-29) */
423  .price-calculator {
424    background: var(--bg-elev, #0c0f17);
425    border: 1px solid var(--rule, #16202e);
426    border-radius: 8px;
427    padding: 22px 26px;
428    margin: 0 auto 28px;
429    max-width: 760px;
430  }
431  .price-calculator .calc-intro {
432    margin: 0 0 14px;
433    font-size: 13px;
434    color: var(--fg-mid, #8d99ad);
435    letter-spacing: .04em;
436    text-transform: uppercase;
437  }
438  .price-calculator .calc-grid {
439    display: grid;
440    grid-template-columns: repeat(3, 1fr);
441    gap: 14px;
442  }
443  .price-calculator .calc-cell {
444    display: flex;
445    flex-direction: column;
446    gap: 4px;
447    padding: 14px 16px;
448    background: var(--bg, #08080d);
449    border: 1px solid var(--rule, #16202e);
450    border-radius: 6px;
451  }
452  .price-calculator .calc-cell .cad {
453    font-size: 11.5px;
454    color: var(--fg-dim, #5a6678);
455    letter-spacing: .08em;
456    text-transform: uppercase;
457  }
458  .price-calculator .calc-cell .est {
459    font-size: 18px;
460    color: var(--fg, #d8dfeb);
461    font-weight: 500;
462  }
463  .price-calculator .calc-foot {
464    margin: 14px 0 0;
465    font-size: 12px;
466    color: var(--fg-dim, #5a6678);
467  }
468  @media (max-width: 640px) {
469    .price-calculator .calc-grid { grid-template-columns: 1fr; }
470  }
471
472  /* ── Phase 1 additions: sandbox / coverage / per-asset / about ─────── */
473
474  /* SANDBOX SECTION ----------------------------------------------- */
475  .sandbox-section .sandbox-lede {
476    max-width: 720px; color: var(--fg-mid); font-size: 15px;
477    margin: 4px 0 28px; line-height: 1.65;
478  }
479  .sandbox-steps { list-style: none; padding: 0; margin: 0;
480                   display: flex; flex-direction: column; gap: 18px; }
481  .sandbox-steps > li { display: flex; gap: 18px;
482                        background: var(--bg-elev);
483                        border: 1px solid var(--rule);
484                        border-left: 2px solid var(--jade);
485                        border-radius: 5px; padding: 22px 24px; }
486  .sandbox-steps .step-num { font-family: 'JetBrains Mono', monospace;
487                             font-size: 13px; color: var(--jade-soft);
488                             letter-spacing: 0.18em; padding-top: 2px;
489                             min-width: 28px; }
490  .sandbox-steps .step-body { flex: 1; }
491  .sandbox-steps h3 { font-family: 'JetBrains Mono', monospace;
492                      font-size: 13px; color: var(--fg);
493                      letter-spacing: 0.02em; text-transform: none;
494                      margin: 0 0 6px; font-weight: 500; }
495  .sandbox-steps p { color: var(--fg-mid); margin: 0 0 12px;
496                     font-size: 14px; line-height: 1.6; }
497  .sandbox-resp { margin-top: 12px; font-size: 12.5px; }
498  .sandbox-foot { margin-top: 22px; color: var(--fg-dim);
499                  font-size: 12.5px; max-width: 720px; line-height: 1.65; }
500
501  /* COVERAGE SECTION ---------------------------------------------- */
502  .coverage-section .coverage-lede {
503    max-width: 720px; color: var(--fg-mid); font-size: 14px;
504    margin: 4px 0 26px; line-height: 1.65;
505  }
506  .oem-grid { display: grid; grid-template-columns: repeat(4, 1fr);
507              gap: 10px; margin: 0 0 22px; }
508  .oem { display: flex; flex-direction: column; gap: 6px;
509         background: var(--bg-elev); border: 1px solid var(--rule);
510         border-radius: 4px; padding: 14px 16px; }
511  .oem-name { font-family: 'JetBrains Mono', monospace;
512              font-size: 13px; color: var(--fg); font-weight: 500;
513              letter-spacing: 0.01em; }
514  .oem-tier { font-family: 'JetBrains Mono', monospace;
515              font-size: 10.5px; letter-spacing: 0.12em;
516              text-transform: uppercase; font-weight: 500; }
517  .oem-tier.prod  { color: var(--jade-soft); }
518  .oem-tier.beta  { color: var(--amber); }
519  .oem-tier.early { color: var(--fg-dim); }
520  .coverage-foot { color: var(--fg-mid); font-size: 12.5px;
521                   max-width: 720px; line-height: 1.65;
522                   border-top: 1px dashed var(--rule); padding-top: 16px; }
523  @media (max-width: 760px) { .oem-grid { grid-template-columns: repeat(2, 1fr); } }
524
525  /* PRICING (Phase 1, per-asset headline) ------------------------ */
526  .pricing-lede { max-width: 720px; color: var(--fg-mid);
527                  font-size: 14px; margin: 4px 0 26px; line-height: 1.65; }
528  .pricing-tier-grid { grid-template-columns: 1fr 1.15fr 1fr; }
529  .tier-free       { border-top: 1px solid var(--rule); }
530  .tier-perasset   { border-top: 2px solid var(--jade);
531                     box-shadow: 0 0 0 1px rgba(26,144,88,.22),
532                                 0 8px 32px rgba(26,144,88,.10); }
533  .tier .price-display { display: flex; align-items: baseline; gap: 6px;
534                         font-family: 'JetBrains Mono', monospace; }
535  .tier .price-display .amount { font-size: 32px; color: var(--fg);
536                                 font-weight: 500; letter-spacing: -0.02em; }
537  .tier .price-display .unit   { font-size: 12px; color: var(--fg-dim);
538                                 letter-spacing: 0; }
539  .tier-features { list-style: none; padding: 0; margin: 6px 0 4px;
540                   display: flex; flex-direction: column; gap: 6px; }
541  .tier-features li { color: var(--fg-mid); font-size: 13px;
542                      line-height: 1.5; padding: 4px 0;
543                      border-bottom: 1px dashed var(--rule-soft); }
544  .tier-features li:last-child { border-bottom: none; }
545  .tier-features strong { color: var(--fg); font-weight: 500; }
546  .tier-features code   { font-size: 12.5px; }
547  .gate-tag { font-family: 'JetBrains Mono', monospace; font-size: 9.5px;
548              letter-spacing: 0.14em; text-transform: uppercase;
549              color: var(--amber); border: 1px solid var(--amber);
550              padding: 1px 6px; border-radius: 3px; margin-left: 4px; }
551  .tier-perasset .cta { background: var(--jade); color: #050609;
552                        box-shadow: 0 0 0 1px var(--jade),
553                                    0 4px 18px rgba(26,144,88,.20); }
554  .tier-perasset .cta:hover { background: var(--jade-soft); }
555  .onramp { display: flex; flex-direction: column; gap: 6px;
556            margin: 24px 0 4px; padding: 14px 18px;
557            background: var(--bg-code); border: 1px solid var(--rule);
558            border-radius: 4px; }
559  .onramp-label { font-family: 'JetBrains Mono', monospace; font-size: 11px;
560                  color: var(--fg-mid); letter-spacing: 0.18em;
561                  text-transform: uppercase; }
562  .onramp-rates { font-family: 'JetBrains Mono', monospace; font-size: 12.5px;
563                  color: var(--fg); }
564  .onramp-rates code { color: var(--jade-soft); }
565  .onramp-foot { color: var(--fg-dim); font-size: 12px; line-height: 1.55; }
566  @media (max-width: 900px) { .pricing-tier-grid { grid-template-columns: 1fr; } }
567
568  /* ABOUT + SECURITY ---------------------------------------------- */
569  .about-section .about-grid { display: grid;
570                               grid-template-columns: 1fr 1fr; gap: 28px; }
571  .about-section h3 { font-family: 'JetBrains Mono', monospace;
572                      font-size: 11px; color: var(--jade-soft);
573                      letter-spacing: 0.18em; text-transform: uppercase;
574                      font-weight: 500; margin-bottom: 14px; }
575  .about-section .about-col p { color: var(--fg-mid); font-size: 14px;
576                                line-height: 1.65; }
577  .about-section .muted { color: var(--fg-dim); font-size: 13px; }
578  .security-list { list-style: none; padding: 0; margin: 0 0 12px;
579                   display: flex; flex-direction: column; gap: 6px; }
580  .security-list li { color: var(--fg-mid); font-size: 13.5px;
581                      line-height: 1.55; padding: 4px 0;
582                      border-bottom: 1px dashed var(--rule-soft); }
583  .security-list li:last-child { border-bottom: none; }
584  .security-list strong { color: var(--fg); font-weight: 500; }
585  @media (max-width: 760px) { .about-section .about-grid { grid-template-columns: 1fr; } }
586
587  /* hide the now-superseded calculator (left in DOM for nobody, replaced) */
588  .price-calculator { display: none; }
589
590  /* 4-DOOR HUB ----------------------------------------------------- */
591  .hub { display:grid; gap:16px; grid-template-columns:1fr 1fr; margin-top:30px; }
592  .product-card { border:1px solid var(--rule); border-top:2px solid var(--jade);
593                  border-radius:6px; padding:22px 22px 18px; background:var(--bg-elev);
594                  display:flex; flex-direction:column; }
595  .product-card h4 { font-family:'JetBrains Mono',monospace; font-size:16px;
596                     font-weight:600; color:var(--fg); margin:0 0 8px; letter-spacing:-.01em; }
597  .product-card p  { color:var(--fg-mid); font-size:13.5px; line-height:1.55; margin:0 0 14px; flex:1; }
598  .product-card a  { font-family:'JetBrains Mono',monospace; font-size:12px; letter-spacing:.02em; }
599  @media (max-width:640px){ .hub { grid-template-columns:1fr; } }
600
601/* ── responsive hardening: mobile horizontal-overflow fixes ─────────────── */
602/* grid/flex children default to min-width:auto and refuse to shrink below their
603   min-content width; a single long token then widens the whole document. */
604.card{min-width:0;overflow-wrap:anywhere}
605.pill{max-width:100%;overflow-wrap:anywhere}
606table.map{display:block;overflow-x:auto;max-width:100%}
607pre,pre.code{max-width:100%}
608/* the footer template has no whitespace between links, so the line cannot wrap;
609   a zero-width space after each separator gives it a break opportunity. */
610footer .sep::after{content:"\200B"}
611/* root-page navs only got flex-wrap inside a <768px media query, so at exactly
612   768px the nav ran 3px past the viewport instead of wrapping. */
613.topnav{flex-wrap:wrap}
614</style>
615<!-- FN-FAVICON -->
616<link rel="icon" href="/favicon.ico" type="image/x-icon" sizes="16x16 32x32 48x48">
617<link rel="icon" href="/icon-512.png" type="image/png" sizes="512x512">
618<link rel="apple-touch-icon" href="/apple-touch-icon.png">
619<!-- /FN-FAVICON -->
620<!-- FN-CDM-LD -->
621<script type="application/ld+json">{
622  "@context": "https://schema.org",
623  "@type": "SoftwareApplication",
624  "name": "Forge Canonical Data Model",
625  "applicationCategory": "Industrial IoT",
626  "operatingSystem": "Docker (linux/amd64, linux/arm64)",
627  "description": "Canonical data model for industrial equipment telemetry with ISA-95 classification",
628  "url": "https://foundrynet.io/cdm/",
629  "offers": {
630    "@type": "Offer",
631    "price": "0",
632    "priceCurrency": "USD",
633    "description": "Free developer sandbox container"
634  },
635  "publisher": {
636    "@type": "Organization",
637    "name": "Foundry Labs",
638    "url": "https://foundrynet.io"
639  }
640}</script>
640
641</head>
642<body>
643<!-- FN-NAV-OPEN -->
644<nav class="topnav">
645  <a class="topnav-brand" href="/">FoundryNet</a>
646  <details class="topnav-dropdown">
647    <summary>Industries</summary>
648    <div class="topnav-menu">
649      <a href="/cnc">Manufacturing</a>
650      <a href="/datacenters">Data centers</a>
651      <a href="/energy">Energy</a>
652      <a href="/coldchain">Cold chain</a>
653    </div>
654  </details>
655  <span class="topnav-spacer"></span>
656  <a class="topnav-cta" href="https://forge.foundrynet.io">Forge</a>
657  <a class="topnav-cta" href="https://forge.foundrynet.io/pricing">Pricing</a>
658  <a class="topnav-cta" href="/cdm/">Data Model</a>
659  <a class="topnav-cta" href="/docs">Docs</a>
660  <a class="topnav-cta" href="/technology">Technology</a>
661</nav>
662<!-- FN-NAV-CLOSE -->
663<!-- FN-NAV-CLOSE -->
664
665<section class="hero first" style="padding-bottom:0;">
666  <h1 style="font-size:clamp(30px,4vw,44px);">Developer docs</h1>
667  <p class="sub" style="font-size:18px;">The Forge API, the 32-tool MCP server, and the reliability internals. Get a free key at <a href="https://forge.foundrynet.io/pricing">forge.foundrynet.io/pricing</a>: 3 machines, no card.</p>
668</section>
669<section id="docs" class="docs">
670  <h2>API Reference</h2>
671  <p class="docs-intro">
672    All <code>/v1/*</code> requests need <code>Authorization: Bearer fnet_…</code> and (where applicable) <code>Content-Type: application/json</code>. Base URL: <code>https://forge.foundrynet.io</code>.
673  </p>
674
675  <h2>Quick start</h2>
676  <ol>
677    <li>Get a free key instantly, no email gate: <a href="https://forge.foundrynet.io/pricing">forge.foundrynet.io/pricing</a> (Sandbox: 3 machines, no card)</li>
678    <li>Identify a machine: <code>POST /v1/identify</code></li>
679    <li>Send data: <code>POST /v1/normalize</code></li>
680    <li>Connect a tool: <code>POST /v1/tools</code></li>
681    <li>Set up automation: <code>POST /v1/triggers/natural</code></li>
682    <li>Query history: <code>GET /v1/history/{machine_id}</code></li>
683    <li>Cryptographic verification: <code>POST /v1/settle</code></li>
684  </ol>
685
686  <h2>Endpoints</h2>
687
688  <div class="endpoint">
689    <span class="method">POST</span> <code class="path">/v1/identify</code>
690    <p class="desc">Provision or look up a persistent, cryptographically verified identity (machine_id) for one machine. Idempotent on (oem, model, serial).</p>
691<pre><code>{
692  "oem":    "fanuc",
693  "model":  "Robodrill α-D21MiB5",
694  "serial": "R30iB-12345",
695  "site":   "Line 3"
696}</code></pre>
697<pre><code>{
698  "machine_id":     "machine_4de03b",
699  "internal_id": "fanuc:robodrill-a-d21mib5:r30ib-12345",
700  "created":     true,
701  "machine":     { "status": "active" },
702  "first_seen":  "2026-05-04T01:00:00Z"
703}</code></pre>
704  </div>
705
706  <div class="endpoint">
707    <span class="method">POST</span> <code class="path">/v1/normalize</code>
708    <p class="desc">Translate raw OEM telemetry to canonical FCS data. Auto-provisions identity from oem/model/serial when no machine_id is given. Returns triggers_fired[] when any active trigger matches. Optional <code>idempotency_key</code> dedups retries within 24h. Optional <code>observed_at</code> (ISO 8601 controller clock) is stored alongside the server <code>ingested_at</code>.</p>
709<pre><code>{
710  "data":   { "Spindle_Speed": 2200, "spindle_load": 92 },
711  "oem":    "fanuc",
712  "model":  "Robodrill α-D21MiB5",
713  "serial": "R30iB-12345",
714  "observed_at":     "2026-05-05T09:30:15Z",
715  "idempotency_key": "optional-caller-supplied-key"
716}</code></pre>
717<pre><code>{
718  "normalized":      { "spindle_speed_rpm": 2200, "spindle_load_pct": 92 },
719  "field_mappings":  { "spindle_load": { "mapping_id": "6c34…", "canonical": "spindle_load_pct", "confidence": 0.93 } },
720  "fields_total":    2, "fields_renamed":  2, "fields_identity": 0, "fields_unknown":  0,
721  "coverage_pct":    100,
722  "machine_id":      "machine_4de03b",
723  "history_id":      "c419…",
724  "triggers_fired":  [{ "trigger_id": "7f0c…", "trigger_name": "high spindle load", "actual_value": 92 }]
725}</code></pre>
726  </div>
727
728  <div class="endpoint">
729    <span class="method">POST</span> <code class="path">/v1/tools</code>
730    <p class="desc">Register a webhook endpoint as a tool. Triggers reference these by tool_id. <code>auth_secret</code> returned ONCE on create; never read back via GET.</p>
731<pre><code>{
732  "name": "slack-maintenance",
733  "url":  "https://hooks.slack.com/services/T…/B…/xxx",
734  "method": "POST",
735  "payload_template": { "text": "Alert: {{oem}} {{model}}, {{field}} at {{value}}" },
736  "auth_type": "none"
737}</code></pre>
738<pre><code>{ "id": "add2…", "name": "slack-maintenance", "auth_type": "none", "enabled": true }</code></pre>
739  </div>
740
741  <div class="endpoint">
742    <span class="method">POST</span> <code class="path">/v1/triggers/natural</code>
743    <p class="desc">Parse a natural-language instruction into a structured trigger. NEVER auto-activates; returns parsed_trigger for review and explicit POST to /v1/triggers.</p>
744<pre><code>{ "machine_id": "machine_4de03b", "instruction": "Alert maintenance Slack when spindle load exceeds 90." }</code></pre>
745<pre><code>{
746  "parsed_trigger": {
747    "machine_id": "machine_4de03b",
748    "name": "Spindle Load Alert",
749    "condition": { "field": "spindle_load_pct", "op": ">", "value": 90 },
750    "actions":   [{ "tool_id": "add2…" }]
751  },
752  "confirmation_required": true
753}</code></pre>
754  </div>
755
756  <div class="endpoint">
757    <span class="method get">
757GET</span> <code class="path">/v1/history/{machine_id}</code>
758    <p class="desc">Per-machine canonical history. Query params: from, to (ISO-8601), fields (csv), limit (≤1000), summary (bool).</p>
759<pre><code>GET /v1/history/machine_4de03b?limit=5&summary=true</code></pre>
760<pre><code>{
761  "machine_id":  "machine_4de03b",
762  "machine":  { "oem": "fanuc", "model": "Robodrill α-D21MiB5", "site": "Line 3" },
763  "row_count": 2,
764  "avg_coverage": 100,
765  "fields_covered": ["spindle_load_pct", "spindle_speed_rpm", "sensor_readings.coolant_temp"]
766}</code></pre>
767  </div>
768
769  <div class="endpoint">
770    <span class="method">POST</span> <code class="path">/v1/settle</code>
771    <p class="desc">Create a tamper-evident, independently auditable record. Add <code>?batch=true</code> with body <code>{"machine_id": "…"}</code> to hash and roll up all unsettled events for that machine into one verifiable record.</p>
772<pre><code>POST /v1/settle?batch=true
773{ "machine_id": "machine_4de03b" }</code></pre>
774<pre><code>{
775  "machine_id":   "machine_4de03b",
776  "merkle_root":  "d35ae2e…",
777  "event_count":  4,
778  "event_types":  { "identify": 1, "normalize": 3 },
779  "period_start": "2026-08-02T21:00:00Z",
780  "period_end":   "2026-08-02T22:00:00Z",
781  "settlement":   "anchored",
782  "settled_at":   "2026-08-02T22:00:04Z"
783}</code></pre>
784    <p class="desc">Read the machine&rsquo;s verified history back with <code>GET /v1/history/{machine_id}</code>.</p>
785  </div>
786
787  <h2>MCP server</h2>
788  <p>MCP endpoint (streamable HTTP): <code>https://mcp.foundrynet.io/mcp</code></p>
789  <p>Forge exposes <strong>32 MCP tools</strong> over <code>mcp.foundrynet.io/mcp</code>, spanning normalization, prediction, operations analytics, safety guardrails, and governance. <code>GET https://mcp.foundrynet.io/health</code> and <code>GET https://forge.foundrynet.io/health</code> both report <code>32</code>. They work across 14 industrial protocols and <span data-fn="prod.oem_families">18</span> OEM families.</p>
790
791  <h3>Cloud prediction tools</h3>
792  <ul>
793    <li><code>predict_breach</code>: TimesFM forecast of <strong>when</strong> a machine exceeds a threshold, with confidence intervals</li>
794    <li><code>remaining_life</code>: remaining useful life estimate for a machine</li>
795    <li><code>fleet_health</code>: score an entire operation in one call</li>
796    <li><code>predict_batch</code>: batch fleet prediction across many machines</li>
797    <li><code>detect_anomalies</code>: surface out-of-distribution telemetry</li>
798    <li><code>machine_intelligence</code>: complete machine intelligence in one call</li>
799  </ul>
800
801  <h3>Cloud core tools</h3>
802  <ul>
803    <li><code>identify_machine</code>: provision/lookup machine_id from (oem, model, serial)</li>
804    <li><code>normalize_telemetry</code>: raw OEM data → canonical FCS, with auto-provision + trigger eval</li>
805    <li><code>query_machine_history</code>: time-series + summary over canonical_data</li>
806    <li><code>create_automation</code>: natural language → parsed_trigger (review-then-activate)</li>
807    <li><code>activate_automation</code>: arm a parsed_trigger after user confirmation</li>
808    <li><code>list_automations</code>: show all triggers configured for a machine</li>
809    <li><code>disable_automation</code>: pause a trigger without deleting it (reversible)</li>
810    <li><code>delete_automation</code>: soft delete (30-day restorable); <code>?permanent=true</code> for hard delete</li>
811    <li><code>restore_automation</code>: undo a soft delete within 30 days</li>
812    <li><code>query_webhook_history</code>: HTTP status, retry counts, latency for a trigger's deliveries</li>
813    <li><code>verify_on_chain</code>: create a tamper-evident record (single payload OR batched)</li>
814  </ul>
815
816  <h2>Reliability + record integrity</h2>
817
818  <h3>Soft delete</h3>
819  <p><code>DELETE /v1/triggers/{id}</code> defaults to soft delete, sets <code>deleted_at</code>, hides from list/eval, restorable for 30 days via <code>PATCH /v1/triggers/{id}/restore</code>. Add <code>?permanent=true</code> for irrecoverable hard delete.</p>
820
821  <h3>Idempotency</h3>
822  <p><code>/v1/normalize</code> accepts an optional <code>idempotency_key</code>. If absent, server synthesizes <code>sha256(machine_id + canonical(data))[:32]</code>. Repeats within 24h return the original response with <code>deduplicated: true</code> and skip trigger evaluation.</p>
823
824  <h3>Coverage metric</h3>
825  <p>Every <code>/v1/normalize</code> response carries three counters: <code>fields_renamed</code> (source != canonical), <code>fields_identity</code> (source == canonical), <code>fields_unknown</code> (no mapping). <code>coverage_pct = (renamed + identity) / total</code>.</p>
826
827  <h3>Record timestamps</h3>
828  <p><code>observed_at</code> (ISO 8601 from the controller clock) is optional on <code>/v1/normalize</code>. Stored alongside <code>ingested_at</code> (server). When the difference exceeds 60s, response includes <code>clock_skew_warning</code>. Sustained-condition windows reference <code>observed_at</code> when present.</p>
829
830  <h3>Structured errors</h3>
831<pre><code>{
832  "error":  true,
833  "status": 422,
834  "field":  "body.oem",
835  "reason": "Field required",
836  "received_value": {},
837  "suggestion":     "e.g. 'fanuc'",
838  "additional_errors": [ { "field": "body.model", "reason": "Field required" } ]
839}</code></pre>
840
841  <h2 id="system-requirements">System requirements</h2>
842  <p>The Forge kernel runs as a Docker container on any Linux host: cloud, on-premise server, industrial PC, or edge gateway.</p>
843
844  <h3>Minimum (edge deployment)</h3>
845<pre><code>CPU:      2 cores, x86_64 or ARM64
846RAM:      4 GB
847Storage:  10 GB available
848OS:       Linux (Ubuntu 20.04+, Debian 11+, Amazon Linux 2)
849Network:  Ethernet to OT network (for Modbus / OPC UA / SNMP / BACnet)
850          Optional internet for cloud sync and updates
851Runtime:  Docker 20.10+ or AWS Greengrass V2</code></pre>
852
853  <h3>Recommended (production)</h3>
854<pre><code>CPU:      4 cores
855RAM:      8 GB
856Storage:  50 GB (accommodates extended telemetry history)
857Network:  1 Gbps to OT network + internet for CMMS and updates</code></pre>
858
859  <h3>Cloud deployment</h3>
860  <p>Any container host: Railway, AWS ECS, GCP Cloud Run, Azure Container Apps. No special hardware. Connects to equipment via API or protocol bridge.</p>
861
862  <h3>Resource profile</h3>
863<pre><code>Container image:    ~2.2 GB (compressed ~900 MB)
864Runtime memory:     ~1.1 GB RSS steady state, ~1.5 GB peak
865CPU at idle:        &lt; 5% single core
866CPU during predict: ~40% single core for 2-3 seconds
867Bandwidth:          &lt; 1 MB/hour per machine at 1 reading/second</code></pre>
868
869  <h3>What is in the 1.1 GB</h3>
870<pre><code>Normalization engine (BGE-M3 embeddings):  ~600 MB
871Prediction engine (TimesFM):                ~400 MB
872Application + vertical packs + schema:      ~100 MB</code></pre>
873
874  <h3>Supported protocols</h3>
875<pre><code>MQTT / Sparkplug B    (push, real-time streaming)
876Modbus RTU / TCP      (poll, legacy PLCs and sensors)
877OPC UA                (subscribe, modern controllers)
878SNMP                  (poll, servers and network equipment)
879BACnet/IP             (subscribe, building automation)
880ROS2 / DDS            (subscribe, robotics and AMR fleets)
881J1939 / CAN           (listen, vehicles and heavy equipment)
882MAVLink               (listen, drones and aerial vehicles)
883REST API              (push or pull, any HTTP-capable device)
884CSV upload            (batch, historical data import)</code></pre>
885
886  <h3>No new hardware required</h3>
887  <p>Forge connects to your existing sensors, PLCs, and controllers through the protocols they already speak. No proprietary gateways, no sensor replacements, no wiring changes.</p>
888
889  <h2>Pricing</h2>
890  <p><strong style="color:var(--fg)">Building on Forge?</strong> The sandbox is free and runs on your own machine (<code>docker pull ghcr.io/foundrynet/forge-sandbox</code>). Direct API integration is $0.30 per 1,000 normalized telemetry events, on graduated tiers. Embedding the engine in a platform you sell is <a href="https://foundrynet.io/licensing">usage-based licensing</a> at $0.15 per 1,000 events, graduating to $0.03 at scale. Full rate card at <a href="https://forge.foun
890drynet.io/pricing">forge.foundrynet.io/pricing</a>.</p>
891  <p style="margin-top:18px;">The rates below are the <strong>metered</strong> option for developers and AI agents making occasional queries, not monitoring machines (that's the flat plan above). Query <code>GET /v1/usage</code> for live counts and an estimated bill.</p>
892  <p style="margin-top:10px;">A <strong>Normalized Telemetry Event (NTE)</strong> is each individual tag-value pair submitted for resolution: a request carrying N tag-value pairs is N NTEs. Normalize bills on graduated tiers per 1,000 NTE: the first 100M events each month bill at the first-tier rate regardless of total volume, and only events above each threshold bill at the next rate.</p>
893  <ul>
894    <li><code>POST /v1/normalize</code>: <strong>$0.30 per 1,000 NTE</strong>, graduating to $0.06 per 1,000 above 50B/month</li>
895    <li>Trigger fire (per webhook delivery, only on 2xx), <strong>5¢</strong></li>
896    <li><code>POST /v1/settle?batch=true</code>: <strong>2¢</strong> per batch verified (skipped verifications are free)</li>
897    <li><code>GET /v1/history/{machine_id}</code>: free</li>
898    <li><code>POST /v1/identify</code>: free</li>
899    <li><code>POST /v1/triggers/natural</code>: free (setup step; revenue comes from the trigger fires it produces)</li>
900    <li><code>POST /v1/feedback/{mapping_id}/confirm</code> and <code>/correct</code>: free</li>
901    <li>Trigger evaluation (the eval pass during <code>/v1/normalize</code>), free; you only pay for fires that successfully deliver</li>
902  </ul>
903  <p>Get a key instantly, no email gate: <a href="https://forge.foundrynet.io/pricing">start free or go Production at forge.foundrynet.io/pricing</a>. Site licenses for integrators: <a data-email href="#">get in touch</a>.</p>
904</section>
905
906<section id="enterprise" class="about-section">
907  <h2>Enterprise</h2>
908  <div class="about-grid">
909    <div class="about-col">
910      <h3>Fleet-scale predictive intelligence</h3>
911      <p>
912        Per-machine deployment via AWS Greengrass. Cross-OEM normalization for
913        mixed-vendor factories. Batch fleet prediction and remaining-life estimation
914        across your entire operation, every result on a tamper-evident audit trail.
915      </p>
916      <p class="muted">
917        Contact: <a data-email href="#">email us</a>
918      </p>
919    </div>
920    <div class="about-col">
921      <h3>Security</h3>
922      <ul class="security-list">
923        <li><strong>Fernet symmetric encryption</strong> on webhook auth secrets at rest.</li>
924        <li><strong>HMAC-SHA256</strong> signing on every outbound webhook delivery.</li>
925        <li><strong>Supabase Row Level Security</strong> isolates accounts at the database level.</li>
926        <li><strong>API keys hashed</strong> (SHA-256), we store the hash, not the key.</li>
927        <li><strong>Idempotency by content hash</strong> on <code>/v1/normalize</code> prevents duplicate processing.</li>
928      </ul>
929      <p class="muted">Full detail in the <a href="/privacy">Privacy Policy</a>.</p>
930    </div>
931  </div>
932</section>
933
934<!-- ── FOOTER ──────────────────────────────────────────────────────── -->
935<!-- FN-SUBSTACK-FOOTER -->
936<div style="text-align:center; padding:1.5rem 0; border-top:1px solid #333;">
937  <a href="https://substack.com/@themachineagent" target="_blank" rel="noopener"
938     style="color:#8b949e; text-decoration:none; font-size:14px;">
939    Get the thesis behind this infrastructure: <span style="color:#58a6ff;">The Machine Agent</span>, weekly.
940  </a>
941</div>
942<!-- /FN-SUBSTACK-FOOTER -->
943<footer>
944  Foundry Labs<span class="sep">·</span><a data-email href="#" class="foot-mail">contact</a><span class="sep collapse">·</span><a href="/docs">Docs</a><span class="sep">·</span><a href="/terms">Terms</a><span class="sep">·</span><a href="/privacy">Privacy</a><span class="sep">·</span><a href="https://assay.foundrynet.io">Assay</a>
945  <span class="sep">·</span><a href="https://substack.com/@themachineagent" target="_blank">The Machine Agent</a>
946</footer>
947
948<script>
949  (function(){
950    var e = ['forge','foundrynet.io'].join('@');
951    document.querySelectorAll('[data-email]').forEach(function(el){
952      el.textContent = e; el.setAttribute('href', 'mailto:' + e);
953    });
954  })();
955</script>
955
956<!-- FN-COPY-BTN-OPEN -->
957<style>
958/* click-to-copy on code blocks and terminal demos, no dependencies */
959.fn-cb { position: relative; display: block; max-width: 100%; }
960/* reserve a gutter so a long single-line command never runs under the button */
961.fn-cb > .fn-cb-block { padding-right: 86px; }
962.fn-cb-btn {
963  position: absolute; top: 8px; right: 8px; z-index: 3;
964  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
965  font-size: 10.5px; letter-spacing: .09em; text-transform: uppercase;
966  line-height: 1; padding: 6px 9px; border-radius: 5px; cursor: pointer;
967  color: #7c889c; background: #0d1219; border: 1px solid #1e2b3d;
968  -webkit-appearance: none; appearance: none;
969  /* opaque, never translucent: long commands scroll UNDER this button, and a
970     see-through chip lets that text bleed through and reads as a glitch */
971  box-shadow: -10px 0 14px -6px rgba(0,0,0,.95);
972  transition: color .15s ease, border-color .15s ease, background .15s ease;
973}
974.fn-cb:hover .fn-cb-btn { color: #b6c0cf; }
975.fn-cb-btn:hover { color: #d8dfeb; border-color: #1a9058; background: #0f1720; }
976.fn-cb-btn:focus-visible { outline: 1px solid #2dbb78; outline-offset: 2px; color: #d8dfeb; }
977.fn-cb-btn.is-copied { color: #2dbb78; border-color: #1a9058; }
978.fn-cb-btn.is-failed { color: #f0a030; border-color: #f0a030; }
979@media (max-width: 600px) {
980  .fn-cb-btn { top: 6px; right: 6px; padding: 5px 7px; font-size: 10px; letter-spacing: .06em; }
981  .fn-cb > .fn-cb-block { padding-right: 66px; }
982}
983@media print { .fn-cb-btn { display: none; } }
984</style>
985<script>
986(function () {
987  'use strict';
988
989  /* Terminal demos interleave commands (.you) with output (.ok/.feed) and
990     blank spacers (.gap). Copy only the FIRST contiguous run of command lines
991     so a click yields something pasteable, never a command plus its output. */
992  function termText(el) {
993    var kids = el.children, lines = [], started = false;
994    for (var i = 0; i < kids.length; i++) {
995      var isCmd = kids[i].classList.contains('you');
996      if (isCmd) { lines.push(kids[i].textContent); started = true; }
997      else if (started) { break; }
998    }
999    return lines.length ? lines.join('\n') : null;
1000  }
1001
1002  function clean(s) {
1003    /* &nbsp; indents render fine but break a shell paste */
1004    return s.replace(/\u00a0/g, ' ').replace(/[ \t]+$/gm, '').replace(/\s+$/, '');
1005  }
1006
1007  function payload(el) {
1008    var raw = el.classList.contains('term') ? termText(el) : el.textContent;
1009    if (!raw) return null;
1010    raw = clean(raw);
1011    return raw ? raw : null;
1012  }
1013
1014  function legacyCopy(text) {
1015    try {
1016      var ta = document.createElement('textarea');
1017      ta.value = text;
1018      ta.setAttribute('readonly', '');
1019      ta.style.cssText = 'position:fixed;top:0;left:-9999px;opacity:0';
1020      document.body.appendChild(ta);
1021      ta.select();
1022      ta.setSelectionRange(0, ta.value.length);
1023      var ok = document.execCommand('copy');
1024      document.body.removeChild(ta);
1025      return ok;
1026    } catch (e) { return false; }
1027  }
1028
1029  function flash(btn, ok) {
1030    btn.textContent = ok ? 'Copied!' : 'Press \u2318C';
1031    btn.classList.remove('is-copied', 'is-failed');
1032    btn.classList.add(ok ? 'is-copied' : 'is-failed');
1033    clearTimeout(btn._fnTimer);
1034    btn._fnTimer = setTimeout(function () {
1035      btn.textContent = 'Copy';
1036      btn.classList.remove('is-copied', 'is-failed');
1037    }, 2000);
1038  }
1039
1040  function copy(text, btn) {
1041    if (navigator.clipboard && navigator.clipboard.writeText && window.isSecureContext) {
1042      navigator.clipboard.writeText(text).then(
1043        function () { flash(btn, true); },
1044        function () { flash(btn, legacyCopy(text)); }
1045      );
1046    } else {
1047      flash(btn, legacyCopy(text));
1048    }
1049  }
1050
1051  function attach(el) {
1052    if (el.getAttribute('data-fn-cb')) return;
1053    var text = payload(el);
1054    if (!text) return;
1055    el.setAttribute('data-fn-cb', '1');
1056    el.classList.add('fn-cb-block');
1057
1058    /* Wrap rather than position inside: pre/.term are overflow-x:auto, so a
1059       button parented to them would scroll away with the content. */
1060    var wrap = document.createElement('div');
1061    wrap.className = 'fn-cb';
1062    el.parentNode.insertBefore(wrap, el);
1063    wrap.appendChild(el);
1064
1065    var btn = document.createElement('button');
1066    btn.type = 'button';
1067    btn.className = 'fn-cb-btn';
1068    btn.textContent = 'Copy';
1069    btn.setAttribute('aria-label', 'Copy code to clipboard');
1070    btn.addEventListener('click', function (e) {
1071      e.preventDefault();
1072      copy(text, btn);
1073    });
1074    wrap.appendChild(btn);
1075  }
1076
1077  function init() {
1078    var nodes = document.querySelectorAll('pre, .term');
1079    for (var i = 0; i < nodes.length; i++) attach(nodes[i]);
1080  }
1081
1082  if (document.readyState === 'loading') {
1083    document.addEventListener('DOMContentLoaded', init);
1084  } else {
1085    init();
1086  }
1087})();
1088</script>
1088
1089<!-- FN-COPY-BTN-CLOSE -->
1090<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" data-cf-beacon='{"version":"2024.11.0","token":"7d6d2efd170341a38bb6c5e09af862b7","r":1,"spa":2}' crossorigin="anonymous"></script>
vendor: 1 bytes, line 1090
1090
1091<script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a44ef1f6ce4a7e38',t:'MTc5MTA2MTk0Nw=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script>
1091</body>
1092</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.