Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion src/pages/es/terms.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import Legal from '../../layouts/NanLegal.astro';
---

<Legal title="Términos del servicio" updated="July 23, 2026">
<Legal title="Términos del servicio" updated="October 4, 2026">
<p>
NaN está operada por <strong>Helmcode S.L.</strong> (NIF B44788511), Avenida Quinto Centenario 27,
Santiago del Teide, España. Contacto: <a href="mailto:info@helmcode.com">info@helmcode.com</a>.
Expand All @@ -28,6 +28,31 @@ import Legal from '../../layouts/NanLegal.astro';
La membresía es una suscripción mensual con impuestos incluidos, mes a mes, sin compromiso. Puedes
cancelar cuando quieras; el acceso se mantiene hasta el final del periodo pagado.
</p>
<p id="workspace-backups">
<strong>Copias de seguridad de workspaces.</strong> Los workspaces <strong>no tienen copias de seguridad salvo
que el complemento de backups esté activo</strong>. Con el complemento, se toma una instantánea del disco del
workspace una vez al día y se conserva durante 7 días. Las instantáneas son consistentes ante caídas
(crash-consistent): restaurar una equivale a que la máquina se quede sin corriente en el momento en que se
tomó, así que los datos que el workspace aún no había escrito en disco no se incluyen y las bases de datos en
ejecución se recuperan como tras un corte de luz. Restaurar una instantánea recupera todo lo que había en el
disco en ese momento, incluido cualquier acceso que hayas configurado tú (por ejemplo claves SSH o ajustes del
servidor SSH que añadieras); la plataforma solo vuelve a aplicar tu clave SSH actual de NaN. Puedes restaurar
hasta 3 veces por workspace en cualquier periodo de 24 horas; no hay copias bajo demanda. Las instantáneas
solo se pueden restaurar mientras el complemento esté activo: durante un periodo de gracia tienes que asociar
el workspace a un slot activo (o renovar la membresía, si es un workspace gratuito) y volver a activar el
complemento, que se cobra de nuevo. Activar el complemento cobra el primer pago en el momento (prorrateado
hasta la fecha de renovación cuando se añade a la suscripción de un slot); desactivarlo lo elimina de
inmediato sin reembolso del mes en curso. Desactivar el complemento o eliminar el workspace borra todas sus
instantáneas de forma permanente. Si el slot del workspace termina, o un workspace gratuito pierde el
beneficio de la membresía, el complemento deja de cobrarse. Cuando termina la facturación, no se toman nuevas
instantáneas y las existentes se conservan hasta que se elimine el workspace al final de su periodo de
gracia. Si el workspace vuelve a estar al día pero el complemento no se reactiva en un plazo de 14 días, el
complemento termina y sus instantáneas se borran. Las instantáneas conservadas no se eliminan mientras
el complemento esté desactivado. Cuando el complemento se vuelve a activar, solo se pueden restaurar las
instantáneas de los últimos 7 días; las instantáneas conservadas más antiguas se eliminan con la siguiente
copia diaria.
Las instantáneas borradas no se pueden recuperar.
</p>

<h2>Propiedad intelectual</h2>
<p>
Expand Down
22 changes: 21 additions & 1 deletion src/pages/terms.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import Legal from '../layouts/NanLegal.astro';
---

<Legal title="Terms of Service" updated="July 23, 2026">
<Legal title="Terms of Service" updated="October 4, 2026">
<p>
NaN is operated by <strong>Helmcode S.L.</strong> (NIF B44788511), Avenida Quinto Centenario 27,
Santiago del Teide, Spain. Contact: <a href="mailto:info@helmcode.com">info@helmcode.com</a>.
Expand All @@ -28,6 +28,26 @@ import Legal from '../layouts/NanLegal.astro';
Membership is a monthly subscription with taxes included, month to month, with no commitment. You can
cancel anytime; access runs until the end of the paid period.
</p>
<p id="workspace-backups">
<strong>Workspace backups.</strong> Workspaces have <strong>no backups unless the workspace backup add-on is
active</strong>. With the add-on, a snapshot of the workspace disk is taken once a day and kept for 7 days.
Snapshots are crash-consistent: restoring one is equivalent to the machine losing power at the moment the
snapshot was taken, so data the workspace had not yet written to disk is not included and running databases
recover as after a power cut. Restoring a snapshot brings back everything that was on the disk at that time,
including any access you configured yourself (for example SSH keys or SSH server settings you added); only
your current NaN SSH key is re-applied by the platform. You can restore up to 3 times per workspace in any
24 hours; there are no on-demand backups. Snapshots can only be restored while the add-on is active: during a
grace period you must attach the workspace to an active slot (or renew the membership, for a free workspace)
and turn the add-on on again, which is billed again. Enabling the add-on charges the first payment right away
(prorated to the renewal date when it is added to a slot subscription); turning it off removes it immediately
with no refund for the current month. Turning the add-on off or deleting the workspace deletes all its
snapshots permanently. If the workspace's slot ends, or a free workspace loses the membership benefit, the
add-on stops being billed. When billing ends, no new snapshots are taken and existing snapshots are kept until
the workspace is deleted at the end of its grace period. If the workspace is back in good standing but the
add-on is not turned on again within 14 days, the add-on ends and its snapshots are deleted. Kept snapshots are not pruned while
the add-on is off. After the add-on is turned on again, only snapshots from the last 7 days can be restored;
older kept snapshots are deleted with the next daily backup. Deleted snapshots cannot be recovered.
</p>

<h2>Intellectual property</h2>
<p>
Expand Down
110 changes: 110 additions & 0 deletions src/tests/legal/workspaceBackupTerms.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { describe, expect, test } from 'vitest';
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, resolve } from 'node:path';

/**
* Workspace backup add-on (helmcode/nan#130): the terms are the contract the
* UI copy points at. These tests pin the promises that must never drift:
* no backups without the add-on, daily crash-consistent snapshots, 7-day
* retention, deletion on disable/workspace deletion, snapshots kept until the
* workspace is deleted when a slot or benefit ends, and no partial refund.
*/
const here = dirname(fileURLToPath(import.meta.url));
const pages = resolve(here, '../../pages');
const flat = (p: string) => readFileSync(resolve(pages, p), 'utf-8').replace(/\s+/g, ' ');

function section(src: string): string {
const m = src.match(/<p id="workspace-backups">(.*?)<\/p>/);
expect(m, 'workspace-backups paragraph present').toBeTruthy();
return m![1].replace(/<[^>]+>/g, '');
}

describe('terms: workspace backups (EN)', () => {
const src = flat('terms.astro');
const p = section(src);

test('sits under "Membership and payments"', () => {
const h = src.indexOf('<h2>Membership and payments</h2>');
const next = src.indexOf('<h2>', h + 1);
const at = src.indexOf('id="workspace-backups"');
expect(h).toBeGreaterThan(-1);
expect(at).toBeGreaterThan(h);
expect(at).toBeLessThan(next);
});

test('states every promise', () => {
expect(p).toContain('no backups unless the workspace backup add-on is active');
expect(p).toContain('once a day and kept for 7 days');
expect(p).toContain('crash-consistent');
expect(p).toContain('power');
expect(p).toContain('no refund for the current month');
expect(p).toContain('Turning the add-on off or deleting the workspace deletes all its snapshots permanently');
expect(p).toContain('the add-on stops being billed');
expect(p).toContain('including any access you configured yourself');
expect(p).toContain('only your current NaN SSH key is re-applied');
});

test('restore limits and grace rules (product decision 2026-10-02)', () => {
expect(p).toContain('You can restore up to 3 times per workspace in any 24 hours');
expect(p).toContain('there are no on-demand backups');
expect(p).toContain('Snapshots can only be restored while the add-on is active');
expect(p).toContain('attach the workspace to an active slot (or renew the membership, for a free workspace) and turn the add-on on again, which is billed again');
expect(p).toContain('When billing ends, no new snapshots are taken and existing snapshots are kept until the workspace is deleted');
expect(p).toContain('within 14 days');
});

test('after turning the add-on on again only the last 7 days are restorable (cloud-api #295)', () => {
expect(p).toContain('Kept snapshots are not pruned while the add-on is off.');
expect(p).toContain('After the add-on is turned on again, only snapshots from the last 7 days can be restored; older kept snapshots are deleted with the next daily backup.');
// Never promises that an older kept snapshot can be restored.
expect(p).not.toContain('restore the one you need first');
});

test('never promises deletion at slot end (owner rule 2026-10-02)', () => {
const clause = p.slice(p.indexOf('When billing ends')).split('.')[0];
expect(clause).toContain('no new snapshots are taken');
expect(clause).toContain('existing snapshots are kept until the workspace is deleted');
expect(clause).not.toMatch(/snapshots are deleted|deletes all/);
});
});

describe('terms: workspace backups (ES)', () => {
const src = flat('es/terms.astro');
const p = section(src);

test('sits under "Membresía y pagos"', () => {
const h = src.indexOf('<h2>Membresía y pagos</h2>');
const next = src.indexOf('<h2>', h + 1);
const at = src.indexOf('id="workspace-backups"');
expect(h).toBeGreaterThan(-1);
expect(at).toBeGreaterThan(h);
expect(at).toBeLessThan(next);
});

test('states every promise', () => {
expect(p).toContain('no tienen copias de seguridad salvo que el complemento de backups esté activo');
expect(p).toContain('una vez al día y se conserva durante 7 días');
expect(p).toContain('crash-consistent');
expect(p).toContain('sin reembolso del mes en curso');
expect(p).toContain('Desactivar el complemento o eliminar el workspace borra todas sus instantáneas de forma permanente');
expect(p).toContain('el complemento deja de cobrarse');
expect(p).toContain('incluido cualquier acceso que hayas configurado tú');
expect(p).toContain('solo vuelve a aplicar tu clave SSH actual de NaN');
});

test('límites de restauración y reglas de gracia', () => {
expect(p).toContain('Puedes restaurar hasta 3 veces por workspace en cualquier periodo de 24 horas');
expect(p).toContain('no hay copias bajo demanda');
expect(p).toContain('Las instantáneas solo se pueden restaurar mientras el complemento esté activo');
expect(p).toContain('volver a activar el complemento, que se cobra de nuevo');
expect(p).toContain('Cuando termina la facturación, no se toman nuevas instantáneas y las existentes se conservan hasta que se elimine el workspace');
expect(p).toContain('en un plazo de 14 días');
});

test('al reactivar el complemento solo se restauran los últimos 7 días', () => {
expect(p).toContain('Las instantáneas conservadas no se eliminan mientras el complemento esté desactivado.');
expect(p).toContain('Cuando el complemento se vuelve a activar, solo se pueden restaurar las instantáneas de los últimos 7 días; las instantáneas conservadas más antiguas se eliminan con la siguiente copia diaria.');
expect(p).not.toContain('restaura antes la que necesites');
});
});
Loading