Secure S3 File Uploads from AI Agents
intermediatewrapProvider·openais3·3 min read
Secure S3 File Uploads from AI Agents
AWS credentials are some of the most sensitive secrets an agent can hold. wrapS3() routes all S3 operations through the Keystore vault, so your agent never touches an AWS access key or secret.
What you'll build
An agent that generates a report with OpenAI, then uploads it to S3. Both the OpenAI key and AWS credentials are resolved from the vault.
Prerequisites
- A Keystore account with an agent token
- OpenAI and S3 keys in your vault
- An S3 bucket ready to receive uploads
- Node.js 18+
Setup
1
Install dependencies
bash
1
npm install @keystore/sdk openai @aws-sdk/client-s32
Initialize Keystore with both providers
typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import Keystore from "@keystore/sdk";
import { wrapS3 } from "@keystore/sdk/wrap/s3";
import OpenAI from "openai";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
const ks = new Keystore({ agentToken: process.env.KS_TOKEN! });
// interceptAll handles OpenAI
ks.interceptAll();
// wrapS3 handles the S3 client — rewrites endpoint to the vault
const s3 = wrapS3(
new S3Client({ region: "us-east-1" }),
ks
);i
S3 uses request signing rather than Bearer tokens, so it needs the dedicated wrapS3() wrapper. The vault handles AWS Signature V4 signing on your behalf.
3
Generate a report with OpenAI
typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const openai = new OpenAI();
const report = await openai.chat.completions.create({
model: "gpt-4o",
messages: [
{
role: "system",
content: "Generate a concise weekly summary report in markdown format.",
},
{
role: "user",
content: `Summarize this week's activity:
- 15,230 API requests processed
- 99.7% uptime
- 3 new agents onboarded
- Average response time: 142ms
- 2 budget alerts triggered`,
},
],
});
const reportContent = report.choices[0].message.content!;4
Upload to S3
typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const fileName = `reports/weekly-${new Date().toISOString().split("T")[0]}.md`;
await s3.send(
new PutObjectCommand({
Bucket: "your-reports-bucket",
Key: fileName,
Body: reportContent,
ContentType: "text/markdown",
})
);
console.log(`Report uploaded to s3://your-reports-bucket/${fileName}`);
ks.restore();Alternative: setupEnv for S3
If you prefer environment variable configuration over wrapping:
typescript
1
2
3
4
5
6
7
8
import Keystore, { Providers } from "@keystore/sdk";
const ks = new Keystore({ agentToken: process.env.KS_TOKEN! });
ks.setupEnv([Providers.S3]);
// This sets AWS_ENDPOINT_URL_S3 to vault.keystore.com/v1/s3
// The AWS SDK reads this env var automatically
const s3 = new S3Client({ region: "us-east-1" });Full example
typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import Keystore from "@keystore/sdk";
import { wrapS3 } from "@keystore/sdk/wrap/s3";
import OpenAI from "openai";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
async function generateAndUpload() {
const ks = new Keystore({ agentToken: process.env.KS_TOKEN! });
ks.interceptAll();
const s3 = wrapS3(new S3Client({ region: "us-east-1" }), ks);
// Generate
const openai = new OpenAI();
const report = await openai.chat.completions.create({
model: "gpt-4o",
messages: [
{ role: "system", content: "Generate a weekly summary in markdown." },
{
role: "user",
content: "15,230 requests, 99.7% uptime, 3 new agents, 142ms avg latency.",
},
],
});
// Upload
const key = `reports/weekly-${new Date().toISOString().split("T")[0]}.md`;
await s3.send(
new PutObjectCommand({
Bucket: "your-reports-bucket",
Key: key,
Body: report.choices[0].message.content!,
ContentType: "text/markdown",
})
);
console.log(`Uploaded: ${key}`);
ks.restore();
}
generateAndUpload();Security benefits
- No AWS credentials in agent code — access key and secret are vault-only
- Scoped access — the agent token can be limited to specific S3 actions
- Audit trail — every S3 operation is logged with agent ID and object key
- Instant revocation — kill the agent token, all S3 access stops
Next steps
- Add email notifications when reports are ready
- Combine with database access for data-driven reports
- Set up production controls with budgets and webhooks