252 lines
9.3 KiB
Text
252 lines
9.3 KiB
Text
---
|
||
import Hero from "@/components/blocks/hero-1.astro";
|
||
import { Button } from "@/components/ui/button";
|
||
import { Icon } from "@/components/ui/icon";
|
||
import { Section, SectionContent, SectionGrid, SectionProse } from "@/components/ui/section";
|
||
import { Tile, TileContent, TileDescription, TileTitle } from "@/components/ui/tile";
|
||
import BaseLayout from "@/layouts/BaseLayout.astro";
|
||
---
|
||
|
||
<BaseLayout
|
||
title="IF.Trace — API"
|
||
description="Upload a file, get a SHA‑256, and receive a public receipt URL. Includes code examples and a live test page."
|
||
>
|
||
<Hero
|
||
link={{ text: "Back to home", href: "/", icon: "arrow-left" }}
|
||
links={[
|
||
{ text: "Open console", href: "/console/login", icon: "lock" },
|
||
{ text: "Read the whitepaper", href: "/whitepaper/", icon: "file-text" },
|
||
]}
|
||
image={{ src: "/assets/iftrace-diagram.svg", alt: "Upload → hash → receipt → share (example)" }}
|
||
>
|
||
<p>API</p>
|
||
<h1>Upload a file. Get a receipt.</h1>
|
||
<p>
|
||
This is the practical page. Copy/paste the examples, run a real request, and share the receipt URL with a reviewer.
|
||
</p>
|
||
</Hero>
|
||
|
||
<Section aria-label="Quickstart">
|
||
<SectionContent class="items-center">
|
||
<SectionProse class="text-center">
|
||
<p>Quickstart</p>
|
||
<h2>Three steps.</h2>
|
||
<p>1) Login. 2) Create an API key. 3) Upload a file to <code>/api/v1/traces</code>.</p>
|
||
</SectionProse>
|
||
</SectionContent>
|
||
|
||
<SectionGrid class="@3xl:grid-cols-3">
|
||
<Tile variant="floating">
|
||
<TileContent>
|
||
<TileTitle class="text-xl">1) Console</TileTitle>
|
||
<TileDescription>Login and create an API key for your organization.</TileDescription>
|
||
<div class="mt-4 flex flex-wrap gap-2">
|
||
<Button size="sm" href="/console/login">
|
||
<Icon name="lock" />
|
||
Login
|
||
</Button>
|
||
<Button size="sm" variant="outline" href="/console/api-keys">
|
||
<Icon name="key" />
|
||
API keys
|
||
</Button>
|
||
</div>
|
||
</TileContent>
|
||
</Tile>
|
||
|
||
<Tile variant="floating">
|
||
<TileContent>
|
||
<TileTitle class="text-xl">2) Upload</TileTitle>
|
||
<TileDescription>POST a file to <code>/api/v1/traces</code>.</TileDescription>
|
||
<pre class="mt-4 overflow-auto rounded-lg bg-muted p-4 text-xs leading-relaxed text-foreground">curl -fsS -X POST \\
|
||
-H 'X-API-Key: <your_key>' \\
|
||
-F 'file=@./document.pdf' \\
|
||
-F 'expected_sha256=<optional_expected_sha256>' \\
|
||
https://infrafabric.io/api/v1/traces</pre>
|
||
</TileContent>
|
||
</Tile>
|
||
|
||
<Tile variant="floating">
|
||
<TileContent>
|
||
<TileTitle class="text-xl">3) Share</TileTitle>
|
||
<TileDescription>Send the receipt URL to the reviewer. No login required.</TileDescription>
|
||
<pre class="mt-4 overflow-auto rounded-lg bg-muted p-4 text-xs leading-relaxed text-foreground"># Response includes:
|
||
receipt_url: https://infrafabric.io/api/v1/public/receipt/<public_id></pre>
|
||
</TileContent>
|
||
</Tile>
|
||
</SectionGrid>
|
||
</Section>
|
||
|
||
<Section variant="floating" aria-label="Live test">
|
||
<SectionContent class="items-center">
|
||
<SectionProse class="text-center">
|
||
<p>Live test</p>
|
||
<h2>Try an upload right now.</h2>
|
||
<p>Paste your API key, pick a file, optionally provide an expected SHA‑256, and run the call.</p>
|
||
</SectionProse>
|
||
|
||
<div class="mt-8 w-full max-w-4xl rounded-xl border bg-card p-6 text-card-foreground">
|
||
<div class="@3xl:grid @3xl:grid-cols-2 @3xl:gap-6">
|
||
<form id="iftrace-upload" class="space-y-4">
|
||
<div>
|
||
<div class="text-sm font-semibold">API key</div>
|
||
<input
|
||
id="apiKey"
|
||
class="mt-2 w-full rounded-lg border bg-muted px-3 py-2 text-sm text-foreground"
|
||
placeholder="iftr_...."
|
||
autocomplete="off"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<div class="text-sm font-semibold">File</div>
|
||
<input id="file" type="file" class="mt-2 w-full text-sm" />
|
||
</div>
|
||
<div>
|
||
<div class="text-sm font-semibold">Expected SHA‑256 (optional)</div>
|
||
<input
|
||
id="expected"
|
||
class="mt-2 w-full rounded-lg border bg-muted px-3 py-2 text-sm text-foreground"
|
||
placeholder="64 hex characters"
|
||
autocomplete="off"
|
||
/>
|
||
</div>
|
||
<Button type="submit" class="w-full">
|
||
<Icon name="upload" />
|
||
Upload and verify
|
||
</Button>
|
||
<p class="text-xs text-white/60">
|
||
This endpoint returns <code>verified</code> only if you provide <code>expected_sha256</code>. Otherwise it returns the computed hash.
|
||
</p>
|
||
</form>
|
||
|
||
<div>
|
||
<div class="text-sm font-semibold">Result</div>
|
||
<pre
|
||
id="result"
|
||
class="mt-2 min-h-[220px] overflow-auto rounded-lg bg-muted p-4 text-xs leading-relaxed text-foreground"
|
||
>{"{}"}</pre>
|
||
<div id="receiptWrap" class="mt-3 hidden">
|
||
<a id="receiptUrl" class="text-sm text-primary hover:underline" href="#" target="_blank" rel="noreferrer"
|
||
>Open public receipt</a
|
||
>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script is:inline>
|
||
function qs(id) {
|
||
return document.getElementById(id);
|
||
}
|
||
|
||
async function runUpload(ev) {
|
||
ev.preventDefault();
|
||
const apiKey = (qs("apiKey").value || "").trim();
|
||
const fileEl = qs("file");
|
||
const expected = (qs("expected").value || "").trim();
|
||
const out = qs("result");
|
||
const receiptWrap = qs("receiptWrap");
|
||
const receiptUrl = qs("receiptUrl");
|
||
|
||
receiptWrap.classList.add("hidden");
|
||
receiptUrl.href = "#";
|
||
|
||
if (!apiKey) {
|
||
out.textContent = JSON.stringify({ error: "Missing API key" }, null, 2);
|
||
return;
|
||
}
|
||
if (!fileEl.files || fileEl.files.length === 0) {
|
||
out.textContent = JSON.stringify({ error: "Pick a file" }, null, 2);
|
||
return;
|
||
}
|
||
|
||
const fd = new FormData();
|
||
fd.append("file", fileEl.files[0]);
|
||
if (expected) fd.append("expected_sha256", expected);
|
||
|
||
out.textContent = "Uploading…";
|
||
try {
|
||
const res = await fetch("/api/v1/traces", {
|
||
method: "POST",
|
||
headers: { "X-API-Key": apiKey },
|
||
body: fd,
|
||
});
|
||
const body = await res.json().catch(() => ({}));
|
||
out.textContent = JSON.stringify(body, null, 2);
|
||
if (body && body.receipt_url) {
|
||
receiptUrl.href = body.receipt_url;
|
||
receiptWrap.classList.remove("hidden");
|
||
}
|
||
} catch (e) {
|
||
out.textContent = JSON.stringify({ error: String(e || "Request failed") }, null, 2);
|
||
}
|
||
}
|
||
|
||
const form = document.getElementById("iftrace-upload");
|
||
if (form) form.addEventListener("submit", runUpload);
|
||
</script>
|
||
</SectionContent>
|
||
</Section>
|
||
|
||
<Section aria-label="Code examples">
|
||
<SectionContent class="items-center">
|
||
<SectionProse class="text-center">
|
||
<p>Examples</p>
|
||
<h2>Copy/paste for common platforms.</h2>
|
||
</SectionProse>
|
||
|
||
<SectionGrid class="@3xl:grid-cols-2">
|
||
<Tile variant="floating">
|
||
<TileContent>
|
||
<TileTitle class="text-xl">Python (requests)</TileTitle>
|
||
<pre class="mt-3 overflow-auto rounded-lg bg-muted p-4 text-xs leading-relaxed text-foreground">import requests
|
||
|
||
API_KEY = "iftr_..."
|
||
with open("document.pdf", "rb") as f:
|
||
res = requests.post(
|
||
"https://infrafabric.io/api/v1/traces",
|
||
headers={"X-API-Key": API_KEY},
|
||
files={"file": ("document.pdf", f, "application/pdf")},
|
||
data={"expected_sha256": ""}, # optional
|
||
timeout=60,
|
||
)
|
||
print(res.status_code)
|
||
print(res.json())</pre>
|
||
</TileContent>
|
||
</Tile>
|
||
|
||
<Tile variant="floating">
|
||
<TileContent>
|
||
<TileTitle class="text-xl">Node.js (fetch)</TileTitle>
|
||
<pre class="mt-3 overflow-auto rounded-lg bg-muted p-4 text-xs leading-relaxed text-foreground">import fs from "node:fs";
|
||
|
||
const apiKey = "iftr_...";
|
||
const fd = new FormData();
|
||
fd.append("file", new Blob([fs.readFileSync("document.pdf")]), "document.pdf");
|
||
// fd.append("expected_sha256", ""); // optional
|
||
|
||
const res = await fetch("https://infrafabric.io/api/v1/traces", {
|
||
method: "POST",
|
||
headers: { "X-API-Key": apiKey },
|
||
body: fd,
|
||
});
|
||
console.log(res.status);
|
||
console.log(await res.json());</pre>
|
||
</TileContent>
|
||
</Tile>
|
||
</SectionGrid>
|
||
</SectionContent>
|
||
</Section>
|
||
|
||
<Section aria-label="Meaning of verified">
|
||
<SectionContent class="items-center">
|
||
<SectionProse class="text-center">
|
||
<p>Meaning</p>
|
||
<h2>Integrity, not interpretation.</h2>
|
||
<p>
|
||
“Verified” means the computed hash matched an expected hash you provided.
|
||
It does not mean “true”, “compliant”, or “approved”.
|
||
</p>
|
||
</SectionProse>
|
||
</SectionContent>
|
||
</Section>
|
||
</BaseLayout>
|