# Inicio rápido del almacenamiento de objetos
El almacenamiento de objetos de Zaroz habla la API de S3, así que las herramientas que ya usas (la AWS CLI, boto3, el SDK de AWS para JavaScript, rclone, la mayoría de programas de copias de seguridad) funcionan con otro endpoint y tus propias claves. Cada suscripción de almacenamiento de objetos tiene **un bucket**.

## 1. Crea el bucket

1. Abre **Almacenamiento → Almacenamiento de objetos** en la consola y compra un plan (o abre la suscripción que ya tienes).
2. Crea el bucket: ponle un nombre que reconozcas y, si quieres, un **límite de almacenamiento** (de 1 GiB a 100 TiB, 100 GiB por defecto).
3. La página del bucket muestra los cuatro valores que pide cualquier cliente S3:

| Valor | Qué es | Ejemplo |
|---|---|---|
| **Endpoint** | La URL de S3 a la que apuntar tu cliente | la URL de la página de tu bucket |
| **Nombre del bucket** | El nombre real que se usa en las peticiones, `zb-` más 32 caracteres. No es el nombre visible que elegiste. | `zb-3f2a9c...` |
| **Región** | `us-east-1`. Los clientes exigen una región; esta es la que debes darles. No indica dónde están los datos. | `us-east-1` |
| **Direccionamiento** | **Estilo de ruta**: `ENDPOINT/BUCKET/CLAVE`. El estilo de host virtual (`BUCKET.ENDPOINT`) no se sirve. | `forcePathStyle` / `addressing_style: path` |

## 2. Crea una clave de acceso

Abre la pestaña **Claves de acceso** del bucket y crea una clave. Elige su alcance:

- **Lectura y escritura** puede listar, descargar, subir, copiar y borrar objetos.
- **Solo lectura** puede listar y descargar. Úsala para todo lo que solo lee, como una web, un script de despliegue o un compañero que solo necesita mirar.

Una clave solo funciona en su propio bucket. Puedes tener hasta **20** claves activas por bucket; ponle a cada una el nombre de lo que la usa, para poder revocar exactamente esa más adelante.

:::warning{title="El secreto se muestra una sola vez"}

Copia la **clave de acceso secreta** en el momento en que se crea. Zaroz solo guarda una huella y nunca puede volver a mostrarla. Si la pierdes, revoca la clave y crea otra.

:::

Revocar una clave la detiene al instante. No cambia nada más.

## 3. Conéctate

Pon los valores en el entorno (la AWS CLI, boto3 y el SDK de JavaScript leen estas dos variables):

```bash
export AWS_ACCESS_KEY_ID="<tu id de clave de acceso>"
export AWS_SECRET_ACCESS_KEY="<tu clave de acceso secreta>"
export AWS_DEFAULT_REGION="us-east-1"
export ENDPOINT="<el endpoint de la página de tu bucket>"
export BUCKET="<el nombre del bucket de la página de tu bucket>"
```

### AWS CLI

```bash
aws s3 cp ./foto.jpg "s3://$BUCKET/fotos/foto.jpg" --endpoint-url "$ENDPOINT"
aws s3 ls "s3://$BUCKET/fotos/" --endpoint-url "$ENDPOINT"
aws s3 sync ./sitio "s3://$BUCKET/sitio" --endpoint-url "$ENDPOINT"
aws s3 cp "s3://$BUCKET/fotos/foto.jpg" ./copia.jpg --endpoint-url "$ENDPOINT"
```

Los archivos grandes se suben por partes automáticamente. También puedes fijar el endpoint una vez en `~/.aws/config` con `endpoint_url = ...` y `s3 = addressing_style = path`.

### Python (boto3)

```python
import os
import boto3
from botocore.config import Config

s3 = boto3.client(
    "s3",
    endpoint_url=os.environ["ENDPOINT"],
    region_name="us-east-1",
    config=Config(s3={"addressing_style": "path"}),
)

bucket = os.environ["BUCKET"]
s3.put_object(Bucket=bucket, Key="hola.txt", Body=b"hola")
print(s3.get_object(Bucket=bucket, Key="hola.txt")["Body"].read())
for item in s3.list_objects_v2(Bucket=bucket).get("Contents", []):
    print(item["Key"], item["Size"])
s3.upload_file("copia.tar.gz", bucket, "copias/copia.tar.gz")
```

### JavaScript (SDK de AWS v3)

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

const s3 = new S3Client({
  endpoint: process.env.ENDPOINT,
  region: "us-east-1",
  forcePathStyle: true, // obligatorio: el estilo de host virtual no se sirve
  requestChecksumCalculation: "WHEN_REQUIRED", // necesario para subidas prefirmadas, ver abajo
  // las credenciales salen de AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY
});

const Bucket = process.env.BUCKET;
await s3.send(new PutObjectCommand({ Bucket, Key: "hola.txt", Body: "hola" }));
const object = await s3.send(new GetObjectCommand({ Bucket, Key: "hola.txt" }));
console.log(await object.Body.transformToString());
const listing = await s3.send(new ListObjectsV2Command({ Bucket }));
console.log(listing.Contents?.map((o) => o.Key));
```

Cualquier otra herramienta S3 usa los mismos cuatro valores. En rclone, elige el proveedor "Other", fija el endpoint y pon `force_path_style = true`.

## Explora y sube archivos en la consola

La pestaña **Objetos** del bucket es un explorador de archivos: carpetas, subida arrastrando y soltando (los archivos grandes se suben por partes, con progreso y un botón de cancelar, hasta 625 GiB por objeto), descarga, borrado y, en un bucket público, un botón que copia la URL pública de un objeto.

La consola nunca transporta tus archivos. Pide a Zaroz un enlace de corta duración (15 minutos) y tu navegador habla directamente con el almacenamiento, así que se aplican las mismas reglas de facturación que con cualquier cliente S3:

- **Las descargas cuentan como transferencia de datos de salida y se facturan por GiB.** Las vistas previas también: la consola no carga nada por su cuenta (sin miniaturas), muestra una vista previa solo cuando haces clic y pregunta antes con archivos de más de 1 MiB.
- **Las subidas son gratis.**
- Listar carpetas no se factura.

Mientras un bucket supera su límite puedes seguir explorando, descargando y borrando, pero no subir. Mientras está suspendido ves los nombres y tamaños, pero no puedes descargar, subir ni borrar.

## Comparte archivos públicamente

En **Ajustes** del bucket, activa **Lectura pública**. Cualquiera podrá entonces descargar un objeto sin clave en:

```text
ENDPOINT/BUCKET/ruta/al/objeto.jpg
```

- La lectura pública cubre **solo la descarga de objetos**. Nadie puede listar el bucket, subir ni borrar sin una clave, y tus otros buckets no se ven afectados.
- Al desactivarla, el cambio tarda unos minutos en aplicarse. Lo descargado entretanto se factura como salida normal.
- Si solo necesitas compartir un archivo un tiempo, prefiere un enlace prefirmado y deja el bucket privado.

## Enlaces prefirmados

Una URL prefirmada permite a alguien descargar (o subir) un objeto durante un tiempo limitado sin tener una clave. Tu código la genera con su propia clave; después la URL funciona sola.

```bash
aws s3 presign "s3://$BUCKET/informe.pdf" --expires-in 3600 --endpoint-url "$ENDPOINT"
```

```python
url = s3.generate_presigned_url(
    "put_object", Params={"Bucket": bucket, "Key": "subidas/a.png"}, ExpiresIn=900
)
```

```js
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
const url = await getSignedUrl(s3, new GetObjectCommand({ Bucket, Key: "informe.pdf" }), { expiresIn: 3600 });
```

El enlace tiene los permisos de la clave que lo creó, y deja de funcionar si esa clave se revoca o el bucket se suspende.

:::info{title="Subidas prefirmadas en JavaScript"}

Las versiones recientes del SDK de AWS para JavaScript v3 añaden a la URL de subida prefirmada una suma de verificación del cuerpo (vacío), y la subida falla con `BadDigest`. Crea el cliente con `requestChecksumCalculation: "WHEN_REQUIRED"`, como en el ejemplo de arriba, y las subidas prefirmadas funcionan. Las descargas y boto3 no se ven afectados.

:::

## Llamar a tu bucket desde una página web

Los navegadores solo llaman al bucket directamente desde la consola de Zaroz: las reglas CORS del bucket permiten el origen de la consola y nada más, y tus claves no pueden cambiarlas. Una página de tu propio sitio no puede subir ni descargar directamente del bucket. Genera URLs prefirmadas en tu servidor (ver arriba) y pásaselas a la página, o usa la lectura pública para archivos que cualquiera pueda descargar.

## Límites

| | |
|---|---|
| Buckets por suscripción | 1 |
| Claves activas por bucket | 20 |
| Límite de almacenamiento | de 1 GiB a 100 TiB (100 GiB por defecto), modificable en cualquier momento |
| Direccionamiento | Solo estilo de ruta |
| Nombre de región | `us-east-1` |

**El límite de almacenamiento protege tu factura.** Cuando los datos almacenados alcanzan el límite, las subidas se detienen: todas las claves pueden seguir listando y descargando, y las de lectura y escritura pueden seguir **borrando** objetos, así que puedes liberar espacio tú mismo. Borra lo suficiente, o sube el límite en **Ajustes**, y las subidas se reanudan en unos diez minutos. El uso se mide cada hora, así que el límite puede sobrepasarse ligeramente y el cambio puede tardar hasta una hora en verse.

## Qué pasa con tus datos

- **Suscripción impagada o suspendida:** tus claves dejan de funcionar y los enlaces públicos se apagan, pero los datos se conservan. Paga la factura y el acceso vuelve en unos diez minutos.
- **Suscripción cancelada o terminada:** el bucket se programa para borrado y sus datos se **eliminan definitivamente a los 7 días**. Descarga lo que necesites antes.
- **Borras tú el bucket:** se aplica el mismo plazo de 7 días antes de purgar los datos.

## Cuánto cuesta

La factura tiene tres partes, que se muestran en la página de tu plan:

1. Un **mínimo mensual** (el precio del plan), cobrado por adelantado.
2. El **almacenamiento**, por GiB-mes, medido cada hora y promediado durante el mes.
3. Las **descargas (salida)**, por GiB enviado fuera del bucket.

Las subidas (entrada) son gratis. El mínimo cubre primero el almacenamiento y la salida: solo pagas de más por el uso que valga más que el mínimo, así que un bucket pequeño cuesta exactamente el precio del plan. La pestaña **Uso** del bucket muestra el almacenamiento y la salida de este mes.

## Solución de problemas

| Ves | Normalmente |
|---|---|
| `AccessDenied` al subir | La clave es de solo lectura, el bucket superó su límite (borrar sigue funcionando) o la suscripción está suspendida |
| `AccessDenied` al listar un bucket público sin clave | Es lo esperado: la lectura pública sirve objetos, no listados |
| `SignatureDoesNotMatch` o un error de DNS | El cliente usa direccionamiento de host virtual: activa el estilo de ruta |
| `BadDigest` en una subida prefirmada desde JavaScript | Pon `requestChecksumCalculation: "WHEN_REQUIRED"` |
| `NoSuchBucket` | Usaste el nombre visible en lugar del nombre `zb-...` del bucket |
