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
19 changes: 18 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="October 14, 2026">
<Legal title="T茅rminos del servicio" updated="October 16, 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 Down Expand Up @@ -57,6 +57,23 @@ import Legal from '../../layouts/NanLegal.astro';
Las instant谩neas borradas no se pueden recuperar.
</p>

<p id="token-packs">
<strong>Paquetes de tokens.</strong> Un paquete de tokens es una compra 煤nica de tokens extra para un solo modelo;
no es una suscripci贸n y no se renueva. Los tokens comprados son v谩lidos durante 6 meses desde tu 煤ltima compra para
ese modelo: cada nueva compra para el mismo modelo suma sus tokens y ampl铆a todo el saldo restante de ese modelo a
6 meses desde esa compra. Los tokens caducados se pierden. Los tokens extra solo se usan cuando has agotado tu
cuota mensual o del periodo para ese modelo; la cuota se sigue renovando con normalidad y nunca consume ni
reinicia el saldo extra. Los tokens extra solo se pueden usar mientras tengas una membres铆a de inferencia activa
(y el tier premium para los modelos premium). Si tu membres铆a termina o pierdes el acceso a un modelo premium, el
saldo queda en pausa y la fecha de caducidad sigue corriendo. Los paquetes de tokens no son reembolsables. Seg煤n
la normativa de consumo de la UE, el contenido digital est谩 sujeto normalmente a un derecho de desistimiento: los
paquetes de tokens se suministran de inmediato a petici贸n expresa tuya, y aceptas que pierdes tu derecho de
desistimiento una vez suministrados. Un reembolso exigido por ley, o un contracargo o disputa del pago, retira
los tokens abonados por esa compra de tu saldo. Los paquetes de tokens no levantan ning煤n otro l铆mite: siguen
aplic谩ndose la concurrencia, l铆mites de peticiones y uso razonable. Los precios se muestran antes de la compra e
incluyen impuestos; no se a帽ade IVA en el pago.
</p>

<h2 id="workspace-acceptable-use">Uso aceptable de los workspaces</h2>
<p>
Este apartado se aplica desde el 6 de octubre de 2026. Los workspaces son m谩quinas personales para tu propio
Expand Down
18 changes: 17 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="October 14, 2026">
<Legal title="Terms of Service" updated="October 16, 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 Down Expand Up @@ -51,6 +51,22 @@ import Legal from '../layouts/NanLegal.astro';
older kept snapshots are deleted with the next daily backup. Deleted snapshots cannot be recovered.
</p>

<p id="token-packs">
<strong>Token packs.</strong> A token pack is a one-time purchase of extra tokens for a single model; it is not a
subscription and does not renew. Purchased tokens are valid for 6 months from your last purchase for that model:
each new purchase for the same model adds its tokens and extends the whole remaining balance of that model to 6
months from that purchase. Expired tokens are lost. Extra tokens are only used once your monthly or period
allowance for that model is used up; the allowance keeps resetting as usual and never consumes or resets the extra
balance. Extra tokens are only usable while you have an active inference membership (and the premium tier for
premium models). If your membership ends or you lose access to a premium model, the balance is paused and the
expiry date keeps running. Token packs are non-refundable. Under EU consumer law, digital content is normally
subject to a right of withdrawal: token packs are supplied immediately at your express request, and you
acknowledge that you lose your right of withdrawal once they are supplied. A refund required by law, or a
chargeback or payment dispute, removes the tokens credited by that purchase from your balance. Token packs do not
lift any other limit: concurrency, rate limits and fair use still apply. Prices are shown before purchase and
include taxes; no VAT is added at checkout.
</p>

<h2 id="workspace-acceptable-use">Workspace acceptable use</h2>
<p>
This section applies from October 6, 2026. Workspaces are personal machines for your own development work. You
Expand Down
87 changes: 87 additions & 0 deletions src/tests/legal/tokenPacksTerms.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { describe, expect, test } from 'vitest';
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, resolve } from 'node:path';

/**
* Token packs (one-time purchase of extra tokens for one model): the terms are
* the contract the portal copy points at. These tests pin the commitments that
* must never drift: one model per pack, 6-month validity from the last purchase
* of that model (extending the whole remaining balance), consumed only after
* the period allowance, usable only with an active inference membership (and
* premium for premium models), paused otherwise while expiry keeps running, no
* refunds, EU withdrawal waiver on immediate supply, refunds/chargebacks remove
* the credited tokens, other limits still apply, prices with taxes included.
*/
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="token-packs">(.*?)<\/p>/);
expect(m, 'token-packs paragraph present').toBeTruthy();
return m![1].replace(/<[^>]+>/g, '');
}

function sitsUnder(src: string, heading: string) {
const h = src.indexOf(`<h2>${heading}</h2>`);
const next = src.indexOf('<h2', h + 1);
const at = src.indexOf('id="token-packs"');
expect(h).toBeGreaterThan(-1);
expect(at).toBeGreaterThan(h);
expect(at).toBeLessThan(next);
}

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

test('sits under "Membership and payments"', () => sitsUnder(src, 'Membership and payments'));

test('states every commitment', () => {
expect(p).toContain('one-time purchase of extra tokens for a single model');
expect(p).toContain('valid for 6 months from your last purchase for that model');
expect(p).toContain('extends the whole remaining balance of that model');
expect(p).toContain('only used once your monthly or period allowance for that model is used up');
expect(p).toContain('the allowance keeps resetting as usual');
expect(p).toContain('only usable while you have an active inference membership');
expect(p).toContain('premium tier for premium models');
expect(p).toContain('paused and the expiry date keeps running');
expect(p).toContain('Token packs are non-refundable');
expect(p).toContain('supplied immediately at your express request');
expect(p).toContain('you lose your right of withdrawal once they are supplied');
expect(p).toContain('removes the tokens credited by that purchase');
expect(p).toContain('concurrency, rate limits and fair use');
expect(p).toContain('Prices are shown before purchase and include taxes; no VAT is added');
});
});

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

test('sits under "Membres铆a y pagos"', () => sitsUnder(src, 'Membres铆a y pagos'));

test('states every commitment', () => {
expect(p).toContain('compra 煤nica de tokens extra para un solo modelo');
expect(p).toContain('v谩lidos durante 6 meses desde tu 煤ltima compra para ese modelo');
expect(p).toContain('ampl铆a todo el saldo restante de ese modelo');
expect(p).toContain('solo se usan cuando has agotado tu cuota mensual o del periodo para ese modelo');
expect(p).toContain('la cuota se sigue renovando con normalidad');
expect(p).toContain('solo se pueden usar mientras tengas una membres铆a de inferencia activa');
expect(p).toContain('tier premium para los modelos premium');
expect(p).toContain('queda en pausa y la fecha de caducidad sigue corriendo');
expect(p).toContain('Los paquetes de tokens no son reembolsables');
expect(p).toContain('se suministran de inmediato a petici贸n expresa tuya');
expect(p).toContain('pierdes tu derecho de desistimiento una vez suministrados');
expect(p).toContain('retira los tokens abonados por esa compra');
expect(p).toContain('concurrencia, l铆mites de peticiones y uso razonable');
expect(p).toContain('Los precios se muestran antes de la compra e incluyen impuestos; no se a帽ade IVA');
});
});

describe('terms: last updated', () => {
test.each(['terms.astro', 'es/terms.astro'])('%s', (f) => {
expect(flat(f)).toContain('updated="October 16, 2026"');
});
});
5 changes: 3 additions & 2 deletions src/tests/legal/workspaceAcceptableUse.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,9 @@ describe.each(Object.entries(cases))('terms: workspace acceptable use (%s)', (_l

test('marks the date it applies from, and the page date moved with it', () => {
expect(s).toContain(c.since);
// Moved again on 2026-10-14 (open signup rewrote the community section).
expect(src).toContain('updated="October 14, 2026"');
// Moved again on 2026-10-14 (open signup rewrote the community section)
// and on 2026-10-16 (token packs clause).
expect(src).toContain('updated="October 16, 2026"');
});

test.each(c.prohibited)('prohibits: %s', (item) => {
Expand Down
Loading