Skip to content

Compatibilidad con S3

Usa el AWS SDK para subir, descargar y gestionar archivos en IPFS Ninja con el mismo código que usas para Amazon S3.

Endpoint

https://s3.ipfs.ninja

Credenciales

La API S3 utiliza tu clave API de IPFS Ninja para la autenticación. Tu clave API sirve tanto como access key como secret key.

Cómo obtener tus credenciales

  1. Ve a Dashboard > API Keys
  2. Haz clic en Create API key y asígnale un nombre (por ejemplo, «Acceso S3»)
  3. Copia la clave completa inmediatamente — solo se muestra una vez y no puede recuperarse después

Tu clave tiene este formato:

bws_628bba35e9e0079d9ff9c392b1b55a7b
├──────────┘└──────────────────────────┘
 prefix (12 chars)    rest of key

Correspondencia con credenciales AWS

Parámetro AWSValorEjemplo
accessKeyIdPrimeros 12 caracteres de tu clave APIbws_628bba35
secretAccessKeyLa clave API completa (los 36 caracteres)bws_628bba35e9e0079d9ff9c392b1b55a7b
regionSiempre us-east-1us-east-1

WARNING

La clave API completa solo se muestra una vez al crearla. Si la pierdes, elimina la clave y crea una nueva desde la página de API Keys.

Inicio Rápido

javascript
import { S3Client, PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3";

const s3 = new S3Client({
  endpoint: "https://s3.ipfs.ninja",
  credentials: {
    accessKeyId: "bws_628bba35",
    secretAccessKey: "bws_628bba35e9e0079d9ff9c392b1b55a7b"
  },
  region: "us-east-1",
  forcePathStyle: true
});

// Upload a file
const put = await s3.send(new PutObjectCommand({
  Bucket: "my-project",
  Key: "hello.json",
  Body: JSON.stringify({ hello: "IPFS" }),
  ContentType: "application/json"
}));

console.log("CID:", put.Metadata?.cid);
// CID: bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi

Buckets = Carpetas

Los buckets de S3 corresponden a tus carpetas de IPFS Ninja. Cuando subes un archivo a un bucket, se almacena en la carpeta correspondiente. Cuando listas los objetos de un bucket, ves los archivos de esa carpeta.

Operación S3Equivalente en IPFS Ninja
CreateBucketCrear una nueva carpeta
ListBucketsListar tus carpetas
DeleteBucketEliminar una carpeta y todos sus archivos
PutObject en bucketSubir archivo a la carpeta
ListObjectsV2 en bucketListar archivos en la carpeta
javascript
import { ListBucketsCommand, CreateBucketCommand, PutObjectCommand } from "@aws-sdk/client-s3";

// Create a bucket (= create a folder)
await s3.send(new CreateBucketCommand({ Bucket: "nft-metadata" }));

// Upload a file into the folder
await s3.send(new PutObjectCommand({
  Bucket: "nft-metadata",      // ← folder name
  Key: "token-42.json",        // ← filename within the folder
  Body: JSON.stringify({ name: "My NFT #42" })
}));

// List buckets (= list your folders)
const { Buckets } = await s3.send(new ListBucketsCommand({}));
console.log(Buckets);
// [{ Name: "nft-metadata", CreationDate: "2026-04-13T..." }]

TIP

Las carpetas creadas a través de la API S3 son las mismas carpetas visibles en tu Dashboard. Puedes organizar archivos desde la API S3, la API REST o la interfaz web — todas comparten el mismo sistema de carpetas.

INFO

A diferencia de Amazon S3, las carpetas de IPFS Ninja son planas por defecto. Para crear estructuras anidadas, usa los endpoints de carpetas de la API REST con parentFolderId. Desde la API S3, usa prefijos de clave (por ejemplo, images/photo.png) para organizar dentro de una carpeta.

Los nombres de bucket son únicos globalmente

Los nombres de bucket viven en un espacio de nombres global compartido por todos los clientes, igual que la semántica de AWS S3. Esto significa que:

  • El primer usuario que crea un bucket con un nombre determinado reclama ese nombre a nivel global.
  • Las llamadas posteriores a CreateBucket con el mismo nombre desde cualquier cuenta devuelven BucketAlreadyExists (409).
  • Si intentas volver a crear tu propio bucket, obtienes BucketAlreadyOwnedByYou (409).
  • El nombre de tu carpeta en el dashboard es por cuenta y puede seguir siendo cualquiera — solo el nombre del bucket visible en S3 pasa por el espacio de nombres global.

Si el nombre que quieres ya está en uso, elige uno con un ámbito distinto (myapp-photos-2026, acme-nft-metadata) — la misma convención que usarías en Amazon S3.

Operaciones Soportadas

PutObject

Sube un archivo a IPFS. El archivo se fija, se escanea por seguridad, y el CID se devuelve en los encabezados ETag y x-amz-meta-cid.

Para importar un archivo CAR en lugar de un archivo normal, añade el encabezado de metadato x-amz-meta-import: car. Consulta Importación CAR para más detalles.

javascript
import { PutObjectCommand } from "@aws-sdk/client-s3";
import fs from "fs";

const result = await s3.send(new PutObjectCommand({
  Bucket: "my-project",
  Key: "photo.png",
  Body: fs.readFileSync("photo.png"),
  ContentType: "image/png"
}));

console.log("CID:", result.ETag);
bash
# curl equivalent
curl -X PUT "https://s3.ipfs.ninja/my-project/photo.png" \
  --data-binary @photo.png \
  -H "Content-Type: image/png" \
  --aws-sigv4 "aws:amz:us-east-1:s3" \
  --user "bws_628bba35:bws_628bba35e9e0079d9ff9c392b1b55a7b"

GetObject

Descarga un archivo por su clave (nombre de archivo) o CID.

javascript
import { GetObjectCommand } from "@aws-sdk/client-s3";

const result = await s3.send(new GetObjectCommand({
  Bucket: "my-project",
  Key: "photo.png"
}));

const body = await result.Body.transformToByteArray();
console.log("Size:", body.length);
console.log("CID:", result.Metadata?.cid);

HeadObject

Obtiene los metadatos de un archivo sin descargar el contenido.

javascript
import { HeadObjectCommand } from "@aws-sdk/client-s3";

const head = await s3.send(new HeadObjectCommand({
  Bucket: "my-project",
  Key: "photo.png"
}));

console.log("Size:", head.ContentLength);
console.log("Type:", head.ContentType);
console.log("CID:", head.Metadata?.cid);

DeleteObject

Elimina el pin de un archivo de IPFS y lo borra de tu cuenta.

javascript
import { DeleteObjectCommand } from "@aws-sdk/client-s3";

await s3.send(new DeleteObjectCommand({
  Bucket: "my-project",
  Key: "photo.png"
}));

ListObjectsV2

Lista los archivos de un bucket con filtrado opcional por prefijo y paginación.

javascript
import { ListObjectsV2Command } from "@aws-sdk/client-s3";

const list = await s3.send(new ListObjectsV2Command({
  Bucket: "my-project",
  Prefix: "images/",
  MaxKeys: 100
}));

for (const obj of list.Contents ?? []) {
  console.log(obj.Key, obj.Size, obj.ETag); // ETag = CID
}

Multipart Upload

Sube archivos grandes (hasta 5 GB) usando multipart upload. El AWS SDK lo gestiona automáticamente:

javascript
import { Upload } from "@aws-sdk/lib-storage";
import fs from "fs";

const upload = new Upload({
  client: s3,
  params: {
    Bucket: "my-project",
    Key: "large-dataset.tar.gz",
    Body: fs.createReadStream("large-dataset.tar.gz"),
    ContentType: "application/gzip"
  },
  partSize: 10 * 1024 * 1024, // 10 MB per part
});

upload.on("httpUploadProgress", (progress) => {
  console.log(`Uploaded ${progress.loaded} of ${progress.total} bytes`);
});

const result = await upload.done();
console.log("CID:", result.ETag);

O controla las partes manualmente:

javascript
import {
  CreateMultipartUploadCommand,
  UploadPartCommand,
  CompleteMultipartUploadCommand
} from "@aws-sdk/client-s3";

// 1. Start
const { UploadId } = await s3.send(new CreateMultipartUploadCommand({
  Bucket: "my-project",
  Key: "big-file.bin"
}));

// 2. Upload parts
const part1 = await s3.send(new UploadPartCommand({
  Bucket: "my-project",
  Key: "big-file.bin",
  UploadId,
  PartNumber: 1,
  Body: chunk1
}));

// 3. Complete
const result = await s3.send(new CompleteMultipartUploadCommand({
  Bucket: "my-project",
  Key: "big-file.bin",
  UploadId,
  MultipartUpload: {
    Parts: [{ PartNumber: 1, ETag: part1.ETag }]
  }
}));

Ejemplo en Python

python
import boto3

s3 = boto3.client(
    "s3",
    endpoint_url="https://s3.ipfs.ninja",
    aws_access_key_id="bws_628bba35",
    aws_secret_access_key="bws_628bba35e9e0079d9ff9c392b1b55a7b",
    region_name="us-east-1"
)

# Upload
s3.put_object(
    Bucket="my-project",
    Key="data.json",
    Body=b'{"hello": "IPFS"}',
    ContentType="application/json"
)

# List files
response = s3.list_objects_v2(Bucket="my-project")
for obj in response.get("Contents", []):
    print(obj["Key"], obj["Size"])

# Download
result = s3.get_object(Bucket="my-project", Key="data.json")
print(result["Body"].read())

Ejemplo en Go

go
package main

import (
    "context"
    "fmt"
    "strings"

    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/credentials"
    "github.com/aws/aws-sdk-go-v2/service/s3"
)

func main() {
    client := s3.New(s3.Options{
        BaseEndpoint: aws.String("https://s3.ipfs.ninja"),
        Region:       "us-east-1",
        Credentials:  credentials.NewStaticCredentialsProvider("bws_628bba35", "bws_628bba35e9e0...", ""),
        UsePathStyle: true,
    })

    _, err := client.PutObject(context.TODO(), &s3.PutObjectInput{
        Bucket:      aws.String("my-project"),
        Key:         aws.String("hello.txt"),
        Body:        strings.NewReader("Hello, IPFS!"),
        ContentType: aws.String("text/plain"),
    })
    if err != nil {
        panic(err)
    }
    fmt.Println("Uploaded!")
}

Configurar CORS (acceso desde SDK en navegador)

Si llamas a la API S3 directamente desde JavaScript en el navegador (SPA, wallet app, herramienta del dashboard), necesitas configurar CORS en el bucket primero. De lo contrario, los navegadores bloquean el preflight y tus subidas fallan con No 'Access-Control-Allow-Origin' header is present.

Misma estructura que AWS S3 — subrecursos PutBucketCors / GetBucketCors / DeleteBucketCors:

PutBucketCors

javascript
import { S3Client, PutBucketCorsCommand } from "@aws-sdk/client-s3";

await s3.send(new PutBucketCorsCommand({
  Bucket: "my-bucket",
  CORSConfiguration: {
    CORSRules: [{
      AllowedOrigins: ["http://localhost:3000", "https://myapp.com"],
      AllowedMethods: ["GET", "HEAD", "PUT", "POST", "DELETE"],
      AllowedHeaders: ["*"],
      ExposeHeaders: ["ETag", "x-amz-meta-cid", "x-amz-request-id"],
      MaxAgeSeconds: 3600,
    }],
  },
}));

GetBucketCors

javascript
import { GetBucketCorsCommand } from "@aws-sdk/client-s3";

const { CORSRules } = await s3.send(new GetBucketCorsCommand({ Bucket: "my-bucket" }));
console.log(CORSRules);

DeleteBucketCors

javascript
import { DeleteBucketCorsCommand } from "@aws-sdk/client-s3";

await s3.send(new DeleteBucketCorsCommand({ Bucket: "my-bucket" }));

Límite y valores por defecto

  • Hasta 5 reglas por bucket (la especificación permite 100; nosotros limitamos a 5 para mantener compacta la respuesta de preflight del navegador).
  • Configuración total limitada a 64 KB serializados.
  • Si no hay configuración CORS establecida, los preflights de navegador son rechazados — igual que la postura por defecto de AWS S3. Configura reglas explícitas para los orígenes desde los que realmente sirves contenido.

Nota real sobre seguridad

CORS es una capa de conveniencia del lado del navegador, no un límite de seguridad. Cada llamada a la API S3 sigue requiriendo una firma SigV4 válida calculada a partir de tu clave API — una configuración CORS permisiva no permite que cualquiera use tu bucket sin esa credencial. Lo que CORS sí evita: que un origen no deseado (por ejemplo, una copia obsoleta de tu app en dev.myapp.com) envíe solicitudes firmadas desde un contexto de navegador.

Alternativa desde el Dashboard

La página de Archivos tiene una opción S3 CORS en el menú de acciones de cada carpeta. Es el mismo almacenamiento subyacente; si no quieres escribir código con PutBucketCors, configúralo ahí.

Diferencias con Amazon S3

CaracterísticaAmazon S3IPFS Ninja S3
Modelo de almacenamientoObjetos mutablesDireccionamiento por contenido (CIDs inmutables)
Comportamiento al sobrescribirReemplaza el objeto en el sitioCrea un nuevo CID, el CID anterior sigue accesible
VersionadoSoportadoNo soportado (usa CIDs para versionar)
Cifrado del lado del servidorSoportadoNo soportado (el contenido está en IPFS)
Políticas de ciclo de vidaSoportadasNo soportadas
Políticas de bucket / ACLsSoportadasUsa modos de acceso del gateway
URLs prefirmadasSoportadasUsa tokens de subida firmados
Tamaño máximo de objeto5 TB5 GB (multipart), 100 MB (PUT individual)
RegionesMulti-regionSolo us-east-1
Valor de ETagHash MD5IPFS CID
Headers adicionalesS3 estándarx-amz-meta-cid (IPFS CID)
Formato de CIDN/DCIDv1 moderno (bafy…) para nuevas subidas; el Qm… heredado sigue siendo válido como entrada
Espacio de nombres de bucketGlobal (en todo AWS)Global (en todas las cuentas de IPFS Ninja) — misma semántica
CORSPutBucketCors soportadoPutBucketCors soportado (límite de 5 reglas)

Migración desde Amazon S3

Reemplaza la configuración de tu cliente S3:

diff
 const s3 = new S3Client({
+  endpoint: "https://s3.ipfs.ninja",
   credentials: {
-    accessKeyId: "AKIA...",
-    secretAccessKey: "wJalrX..."
+    accessKeyId: "bws_628bba35",
+    secretAccessKey: "bws_628bba35e9e0..."
   },
   region: "us-east-1",
+  forcePathStyle: true
 });

Tus llamadas existentes a PutObject, GetObject, ListObjectsV2 y DeleteObject funcionan sin cambios.

Migración desde Filebase

Reemplaza la URL del endpoint:

diff
 const s3 = new S3Client({
-  endpoint: "https://s3.filebase.com",
+  endpoint: "https://s3.ipfs.ninja",
   credentials: {
-    accessKeyId: "FILEBASE_KEY",
-    secretAccessKey: "FILEBASE_SECRET"
+    accessKeyId: "bws_628bba35",
+    secretAccessKey: "bws_628bba35e9e0..."
   },
   region: "us-east-1",
   forcePathStyle: true
 });