paste-text-hosting is a self-hosted, working text-hosting (paste bin) web application. It lets anyone paste text into a large composer on the homepage, submit it to a real backend, and receive a unique, hard-to-guess URL such as https://domain.com/p/AbC123. Anyone who opens that URL sees the stored text exactly as it was written, and every paste also exposes a raw endpoint at /p/:id/raw that returns only the paste body as text/plain.
The product intent is a functioning developer utility, not a mockup: content is genuinely persisted in a database, retrievable by URL, and served through a clear REST API. The concept and user flow are conceptually similar to Pastefy.com, but the design and branding are original — a dark, information-first developer platform built on a visible grid, ruled metadata rows, and wayfinding colour codes rather than a neon or lifestyle-app aesthetic.
The audience is developers, sysadmins, students, and self-hosters who paste code, logs, and configuration all day and need a calm, rigorous, trustworthy place to store and share text. The application is anonymous by design: no accounts, no login, and no passwords or sensitive data stored by default.
paste-text-hosting is delivered as a server-rendered/static frontend plus a real backend and a database. The frontend is HTML, CSS, and JavaScript; the backend is Node.js with Express; the database is SQLite for the simple version. The backend owns paste creation, unique slug generation, persistence, expiration enforcement, visibility handling, and the raw plain-text response. The frontend owns the composer, the success state, and the paste reader with its copy, raw, download, fullscreen, and search controls.
Actors. Two accepted human personas use the product: the Paste Creator, who submits non-empty text with optional metadata and receives a unique URL, and the Paste Reader, who opens a paste URL to read or retrieve the exact original content. There are no other accepted human actors. The backend, the database, and the browser clipboard are non-persona system actors.
Accepted behaviour. Creating a paste (with title, expiration, visibility, and syntax language options), generating a unique non-sequential slug, storing the record, returning the paste URL and raw URL, showing a success state with Copy Link / Open Paste / Raw, rendering the paste page with metadata and unmodified monospace content, serving the raw endpoint as plain text only, downloading the content as a text file, copying to clipboard with a "Copied!" confirmation, enforcing expiration on the backend, distinguishing Public from Unlisted visibility, and applying the stated security controls.
Ownership. All accepted human-facing behaviour is owned by first-party application pages: Home, Create Paste, Paste Created, /p/:id, /p/:id/raw, and About. The REST endpoints /api/pastes, /api/pastes/:id, and /api/pastes/:id/raw are first-party application surfaces that serve the frontend and any external API consumer. No provider-owned or external-destination surfaces are accepted.
Narrow exclusions. This document does not add accounts, login, OAuth, folders, tags, rich previews (Markdown/Mermaid/CSV/GeoJSON/Diff/Calendar/Regex/Asciinema), embeds, QR codes, code screenshots, OS-level social sharing, SDKs, CLI clients, VS Code or Raycast extensions, utility/convert tools, client-side encryption, or an Explore/search listing page. These appear in the inspiration-only reference and are not accepted product scope. The visibility rule's phrase "if a search system exists" is preserved as a conditional and does not itself create a search system.
The product is a first-party, self-hostable web application with a real backend. The user explicitly requires that the site is not frontend-only, that data is genuinely stored in a backend database, that localStorage is not used as the primary database, and that no fake links or mockups are produced. /p/:id and /p/:id/raw must genuinely work.
Access ownership. Every accepted destination is anonymously reachable. The source never asks for accounts, login, or user identity, and it explicitly says not to store passwords or sensitive data by default. There is therefore no application-owned identity, no first-use identity establishment, and no protected destination in the current scope. Paste URLs are the access mechanism: anyone holding a URL can read the paste, and Unlisted pastes are reachable only by direct URL.
Delivery boundary. Current scope is the working paste host described above, runnable on localhost and deployable to hosting with a custom domain. The base URL must come from an environment variable rather than a hardcoded localhost, so the same build serves https://paste.example.com/p/AbC123 and https://paste.example.com/p/AbC123/raw. HTTPS is required in production. Nothing in the current scope is deferred to a future horizon; the future section records only the inspiration-only reference capabilities that were not accepted.
Not applicable. The single reference directive (Pastefy.com) declares uses: ["feature_reference", "structure_reference"] with authority: "inspiration_only" and does not declare content_source. No factual content-source inventory is therefore included, and no Pastefy.com names, copy, features, or facts are carried into product scope.
ENDPOINT /api/pastes, RAW /p/:id/raw, EXPIRY NEVER → 30D, VISIBILITY PUBLIC / UNLISTED) with a yellow "LIVE" chip on the first row.Paste your text here..., a three-cell metadata row (Expiration / Visibility / Syntax) rendered as label-over-value pairs with hard vertical rules, and a red Create Paste button pinned flush right beneath.Paste your text here...; Expiration defaults to Never; Visibility defaults to Public.Please enter some text.; server failure shows Something went wrong. Please try again.; the draft content is preserved so the creator can retry.Please enter some text. for empty content; Something went wrong. Please try again. for server errors; draft retained for recovery.Paste created successfully!, the full paste URL, and the raw URL, presented as ruled label/value rows./p/:id; Raw — navigates to /p/:id/raw.Copied! confirmation), Raw, Download, Back, Fullscreen, and Search text./raw toggle that swaps the view in place and shows the literal text/plain; charset=utf-8 header string as a ruled caption; search control with match highlighting in the wayfinding colour code.404 Paste Not Found; expired paste shows Paste Expired; server failure shows Something went wrong. Please try again.curl, and scripts.Content-Type: text/plain; charset=utf-8; the body is the exact stored content.print("Hello World") returns exactly print("Hello World") and never {"text": "print(\"Hello World\")"}.Paste Not Found; expired paste returns Paste Expired; server failure returns Something went wrong. Please try again.POST /api/pastes → slug → /p/:id → /p/:id/raw as connected nodes on a grid.title, content, language, visibility, and expiration; returns success, id, url, and raw_url.POST — create a paste.{"success": true, "id": "AbC123", "url": "/p/AbC123", "raw_url": "/p/AbC123/raw"}.Something went wrong. Please try again. is the user-facing server-error message.GET — retrieve paste information.Paste Not Found; expired paste returns Paste Expired; server failure returns Something went wrong. Please try again.GET — retrieve raw content.Content-Type: text/plain; charset=utf-8 with the paste body only.Paste Not Found; expired paste returns Paste Expired; server failure returns Something went wrong. Please try again.FR-1 — Working text-hosting website with original design and branding (explicit) As a Paste Creator, I should use a working text-hosting website whose concept and user flow are conceptually similar to Pastefy.com but whose design and branding are original, so that I have a functional, distinct paste host rather than a clone.
Something went wrong. Please try again. Continuation: the creator proceeds to compose a paste.FR-2 — Homepage structure (explicit)
As a Paste Creator, I should see a modern, clean, responsive, mobile-friendly homepage containing a logo/site name, a navbar with Home, Create Paste, and About buttons, a large textarea with the placeholder Paste your text here..., and a Create Paste button, so that I can immediately start a paste.
FR-3 — Optional paste metadata fields (explicit) As a Paste Creator, I should be able to optionally set a paste title, expiration, visibility, and syntax language, laid out as Title, Text textarea, Expiration (default Never), Visibility (default Public), and Create Paste, so that I can describe and control the paste before submitting.
FR-4 — Create Paste flow (explicit) As a Paste Creator, I should have my submission validated, sent to the backend via the API, assigned a unique ID, stored in the database, and answered with the paste URL and a success page, so that my text is genuinely saved and reachable.
Please enter some text.; server failure shows Something went wrong. Please try again. with the draft preserved. Continuation: the creator copies the link, opens the paste, or opens the raw view.FR-5 — Success page content and actions (explicit)
As a Paste Creator, I should see Paste created successfully! with the paste URL and the buttons Copy Link, Open Paste, and Raw, so that I can immediately share or inspect my paste.
/p/:id; Raw goes to /p/:id/raw.Paste created successfully! is shown with the URL and all three buttons.FR-6 — Paste page content (explicit)
As a Paste Reader, I should see the paste title, paste ID, creation time, language/syntax if available, the text content, and Copy, Raw, Download, and Back buttons at /p/:id, so that I can read the paste and act on it.
/p/AbC123. Observable result: metadata and content rendered. Failure/recovery: unknown ID shows 404 Paste Not Found; expired paste shows Paste Expired. Continuation: the reader copies, downloads, opens raw, or goes back./p/:id route.FR-7 — Unmodified monospace content with correct escaping (explicit) As a Paste Reader, I should see the paste content in a monospace font, unmodified, with correct escaping so stored content is never executed as HTML or JavaScript, so that I read exactly what was written.
hello\nworld\n123\n\n<script>\nlocal test()\nend displays exactly as written, and the <script> block is never executed.FR-8 — Raw mode endpoint (explicit)
As a Paste Reader, I should get Content-Type: text/plain with only the paste body from /p/:id/raw, with no navbar, HTML UI, buttons, footer, CSS, or JSON wrapper, so that I can consume the paste as clean data.
/p/AbC123/raw. Observable result: plain-text body only. Failure/recovery: unknown ID returns 404 Paste Not Found; expired paste returns Paste Expired. Continuation: the reader pipes the output into other tools.print("Hello World") returns exactly print("Hello World"), never {"text": "print(\"Hello World\")"}.FR-9 — Download (explicit)
As a Paste Reader, I should download the paste content as a text file from the paste page, named paste-ID.txt or, when a language is chosen, script.lua, script.js, script.py, or script.txt, so that I can keep the content locally.
FR-10 — Copy (explicit)
As a Paste Reader, I should have the paste content copied to my clipboard when I press Copy, with a Copied! confirmation on success, so that I can reuse the text elsewhere.
Copied! is shown. Failure/recovery: if the clipboard write fails, the content remains selectable in the viewer. Continuation: the reader pastes the content elsewhere.Copied! appears on success.FR-11 — Database record structure (explicit)
As the system, I should store each paste with at least id, slug, title, content, language, visibility, created_at, and expires_at, so that every paste is fully persisted and retrievable.
FR-12 — Unique, hard-to-guess slugs (explicit)
As the system, I should generate unique, hard-to-guess IDs/slugs such as /p/a8Kx29, /p/Qm92Ld, and /p/Zx81Pq, and I should never use sequential IDs like /p/1, /p/2, /p/3, so that pastes cannot be enumerated.
FR-13 — POST /api/pastes (explicit)
As an API consumer, I should create a paste by POSTing title, content, language, visibility, and expiration to /api/pastes and receive success, id, url, and raw_url, so that I can create pastes programmatically.
{"title": "My Paste", "content": "Hello World", "language": "text", "visibility": "public", "expiration": null} returns {"success": true, "id": "AbC123", "url": "/p/AbC123", "raw_url": "/p/AbC123/raw"}.FR-14 — GET /api/pastes/:id (explicit)
As an API consumer, I should retrieve paste information from /api/pastes/:id, so that I can inspect a paste's metadata and content programmatically.
Paste Not Found; expired paste returns Paste Expired. Continuation: the consumer uses the data.FR-15 — GET /api/pastes/:id/raw (explicit)
As an API consumer, I should retrieve raw content from /api/pastes/:id/raw with Content-Type: text/plain; charset=utf-8, so that I can consume the paste body without a wrapper.
Paste Not Found; expired paste returns Paste Expired. Continuation: the consumer processes the text.Content-Type: text/plain; charset=utf-8 and the body is the paste content only.FR-16 — Security controls (explicit) As the system, I should escape HTML in content, never execute stored JavaScript, use parameterized queries, validate input, rate limit paste creation, limit paste size, prevent path traversal, use random IDs, protect the API from spam, avoid storing passwords or sensitive data by default, and use HTTPS in production, treating paste content as data rather than executable HTML, so that the service is safe to run publicly.
FR-17 — Expiration options and enforcement (explicit)
As a Paste Creator, I should choose Never, 10 Minutes, 1 Hour, 1 Day, 7 Days, or 30 Days, and expired pastes should show Paste Not Found / Paste Expired, with the backend genuinely checking expiration rather than only hiding it in the frontend, so that time-limited pastes truly expire.
Paste Expired and can navigate away. Continuation: the reader returns Home or opens another paste.FR-18 — Visibility options (explicit) As a Paste Creator, I should choose Public or Unlisted, where Public pastes can be found if a search system exists and Unlisted pastes remain openable via direct URL but do not appear in listings/search, so that I can control discoverability.
FR-19 — Developer-tools design (explicit) As a Paste Creator and Paste Reader, I should use a modern developer-tools interface with dark mode by default, black/dark gray background, modern cards, thin borders, rounded corners, monospace font for text, syntax highlighting when a language is selected, light animation, mobile responsiveness, restrained neon, a professional look, and an uncluttered UI, so that the homepage reads as a modern developer platform.
FR-20 — Paste viewer (explicit) As a Paste Reader, I should read long text comfortably with line numbers, horizontal scrolling, vertical scrolling, Copy, Raw, Download, Fullscreen, and text search, and syntax highlighting must not alter the original paste content, so that long pastes stay readable and faithful.
FR-21 — Responsive mobile behaviour (explicit) As a Paste Reader on Android, I should have a comfortable experience where the textarea fills the screen width, buttons are not too small, the navbar becomes a mobile menu on small screens, and the paste viewer supports horizontal scroll, so that the site is usable on a phone.
FR-22 — Error handling messages (explicit)
As a Paste Reader, I should see 404 Paste Not Found for an unknown ID, Paste Expired for an expired paste, Please enter some text. for empty content, and Something went wrong. Please try again. for server errors, so that I always understand what happened.
FR-23 — Custom domain support (explicit)
As an operator, I should run the system on my own domain such as https://paste.example.com, producing https://paste.example.com/p/AbC123 and https://paste.example.com/p/AbC123/raw, with no hardcoded localhost and an environment variable for the base URL, so that the deployment is portable.
FR-24 — Real backend and simple deployable stack (explicit)
As an operator, I should run a site that is not frontend-only, with a real backend that stores data, using a simple, easy-to-deploy stack such as HTML + CSS + JavaScript frontend, Node.js + Express backend, and SQLite for the simple version, with a clear folder structure including server.js, package.json, database/database.sqlite, public/index.html, public/style.css, public/app.js, and README.md, so that I can run and deploy it easily.
FR-25 — Complete ready-to-run deliverables (explicit)
As an operator, I should receive complete, ready-to-copy-and-run source code covering folder structure, package.json, backend, database setup, frontend, CSS, JavaScript, API, raw system, download system, copy system, expiration system, visibility system, basic security, how to run on localhost, how to deploy to hosting, and how to connect a custom domain, with no partial snippets, so that I can run the whole system immediately.
FR-26 — Genuine functionality, no mockups (explicit)
As a Paste Creator, I should have my paste genuinely stored in the backend and retrievable via URL, with /p/:id and /p/:id/raw really working, and with no mockups, no localStorage as the primary database, and no fake links, so that the product is trustworthy.
FR-27 — Backend database persistence for paste records (required_inference) As the system, I should persist each accepted paste as a durable database record so that it survives process restarts and remains retrievable by slug.
FR-28 — Unique random slug generation (required_inference) As the system, I should generate a random slug and verify its uniqueness before committing the record, so that every paste URL resolves to exactly one paste.
FR-29 — Backend expiration validation during retrieval (required_inference)
As the system, I should evaluate expires_at on every retrieval path — the paste page, the raw route, and both API endpoints — so that expiry is enforced server-side.
expires_at. Observable result: the paste is treated as unavailable. Failure/recovery: the reader sees Paste Expired. Continuation: the reader returns to a working state.FR-30 — Plain-text raw response handling (required_inference)
As the system, I should serialize the raw response as plain text with Content-Type: text/plain; charset=utf-8 and no wrapper, so that consumers receive the paste body exactly.
FR-31 — Configured environment-based deployment URL (required_inference) As the system, I should build absolute paste and raw URLs from a configured base URL environment variable, so that the same build serves any domain.
FR-32 — Input validation, size limits, parameterized queries, rate limiting, and escaping (required_inference) As the system, I should validate all input, enforce a paste size limit, use parameterized queries for every database access, rate limit paste creation, and escape content on output, so that the security requirements are actually enforced.
The Paste Creator is the persona who initiates the product's core transaction. They arrive at the homepage with text in hand — a script, a log excerpt, a configuration file, a snippet they want to hand to someone else — and their recurring responsibility is to submit non-empty content to the backend so that it is genuinely stored and a unique slug/URL is returned.
Their work is defined by the composer: they paste or type into the large textarea, optionally set a title, choose an expiration from Never, 10 Minutes, 1 Hour, 1 Day, 7 Days, or 30 Days, choose Public or Unlisted visibility, and select a syntax language. They then press Create Paste. Their success is the confirmation state showing Paste created successfully! with the paste URL and the Copy Link, Open Paste, and Raw actions.
What makes this role distinct from the reader is that the creator owns the commitment: they decide what is stored, for how long, and how discoverable it is. They are the only persona who experiences the validation failure Please enter some text., the only one who sees the success transition, and the only one who needs the returned URL to share. They interact with the Paste Reader indirectly — the URL they copy is the reader's entry point — and with the system through the create API. Their observable success is a working URL that returns their exact text.
The Paste Reader is anyone who opens a paste URL such as /p/AbC123. They may be the creator returning to their own paste, a colleague who received the link, or a stranger following a shared URL. Their responsibility is to read or retrieve the exact original content.
Their work is defined by the paste page and the raw endpoint. On /p/:id they rely on the metadata header — title, paste ID, creation time, language/syntax when available, visibility, and expiration — and on the code viewer with its line numbers, horizontal and vertical scrolling, search, fullscreen, Copy, Raw, Download, and Back controls. On /p/:id/raw they rely on a plain-text response with no UI at all, suitable for curl and scripts.
What makes this role distinct from the creator is that they hold no authoring responsibility: they cannot change what is stored, and their only decisions are how to consume it — copy it, download it, open the raw line, search within it, or go back. They are the persona who experiences 404 Paste Not Found and Paste Expired, and their observable success is reading or retrieving the exact original content, or receiving a clear, honest error when the paste is unavailable.
Paste your text here..., and a three-cell metadata row for Expiration, Visibility, and Syntax.My Script, selects an expiration (default Never), selects a visibility (default Public), and selects a syntax language.Please enter some text. and the creator returns to step 3.POST /api/pastes with title, content, language, visibility, and expiration.id, slug, title, content, language, visibility, created_at, and expires_at.{"success": true, "id": "AbC123", "url": "/p/AbC123", "raw_url": "/p/AbC123/raw"}.Paste created successfully! with the paste URL and the Copy Link, Open Paste, and Raw buttons.Something went wrong. Please try again. and the draft is preserved so the creator can retry from step 5./p/:id, or Raw to go to /p/:id/raw.Please enter some text.; on server failure they see Something went wrong. Please try again. with the draft preserved./p/AbC123.expires_at.404 Paste Not Found and can use Back or navigate Home.Paste Expired and can use Back or navigate Home.Copied! appears), Download (the browser downloads paste-ID.txt or script.lua / script.js / script.py / script.txt when a language was chosen), Raw (the view swaps in place and shows the literal text/plain; charset=utf-8 header string as a ruled caption), or Back to return to the previous context./p/AbC123/raw, either directly, from the Raw button on the paste page, or from the Raw button on the Paste Created state.expires_at.Paste Not Found.Paste Expired.Content-Type: text/plain; charset=utf-8 and the body is the paste content only — no navbar, no HTML UI, no buttons, no footer, no CSS, and no JSON wrapper.print("Hello World") returns exactly print("Hello World"), never {"text": "print(\"Hello World\")"}.paste-ID.txt, or script.lua, script.js, script.py, or script.txt when the creator selected a language.Copied! is shown.POST /api/pastes with {"title": "My Paste", "content": "Hello World", "language": "text", "visibility": "public", "expiration": null}.{"success": true, "id": "AbC123", "url": "/p/AbC123", "raw_url": "/p/AbC123/raw"}.GET /api/pastes/AbC123 and receives the paste information.GET /api/pastes/AbC123/raw and receives Content-Type: text/plain; charset=utf-8 with the paste body only.Paste Not Found; an expired paste returns Paste Expired; a server failure returns Something went wrong. Please try again.expires_at computed from the chosen expiration and stores the chosen visibility.Paste Expired because the backend genuinely checks expiration.404 Paste Not Found.Paste Expired.Something went wrong. Please try again.server.js, package.json, database/database.sqlite, public/index.html, public/style.css, public/app.js, and README.md.package.json and starts the Node.js + Express server with the SQLite database./p/:id and /p/:id/raw genuinely work.https://paste.example.com.https://paste.example.com/p/AbC123 and raw URLs as https://paste.example.com/p/AbC123/raw.The creative direction is authoritative for this section: Systematic clarity after Massimo Vignelli — a paste bin built like a subway map. The muse is Massimo Vignelli; the headline idea is that a paste host is an information system, so slugs are identifiers, endpoints are routes, expiration is a schedule, visibility is an access level, and raw mode is a clean data line. Dark mode is the default, so Vignelli's white ground inverts to a deep ink ground with the same coded primary accents acting as wayfinding.
| Role | Token | Value |
|---|---|---|
| Background (ink ground, ~70% of screen) | --bg | #0E0E0F |
| Surface (composer, code viewer panels) | --surface | #17181A |
| Body text (only body colour) | --text | #F2F0EC |
| Primary / action code | --primary | #E4322B |
| Accent / metadata code | --accent | #F0C020 |
| Muted / labels, timestamps, helper text | --muted | #8C8C8C |
| Hairline rules | --rule | rgba(242, 240, 236, 0.12) |
No colour is decorative: every hue means something, exactly as a transit line does. Red means "act or live" — Create Paste, the active nav rule, focus rings, the "LIVE" dot on unexpired pastes. Yellow means "metadata" — expiration chips, language tags, the line-number gutter. Grey means "label" — labels, timestamps, helper text. Contrast: #F2F0EC on #0E0E0F is approximately 17:1; #E4322B on #0E0E0F is approximately 4.6:1, so red is used only for text at 18px or larger or for bold text and never for small body copy.
clamp(40px, 7vw, 88px). Section headings 40px desktop / 28px mobile. Body 16px, small 14px, labels 11px uppercase, code 13.5px with 1.65 line-height.Hard-edged and rectilinear. Radius is 0 on section blocks, 2px on inputs and buttons, and 0 on the code viewer (it is a data surface, not a card). Structure comes from 1px rules and negative space, not from shadows: one hairline border per panel, one 3px colour bar as a section marker. No pills, no blobs, no soft continuous curves.
A strict 12-column grid with 24px gutters and a 1280px max content width; every element snaps to it. The homepage reads top-to-bottom as a route: nav rule, oversized headline block, then a two-zone composer — a full-width title input on its own row, a 60vh monospace textarea as the dominant element, and a three-cell metadata row (Expiration / Visibility / Syntax) aligned as label-over-value pairs with hard vertical rules between cells, with the CTA pinned flush right beneath. The paste view is a document, not a card: a metadata header of ruled label/value rows (ID, created, language, visibility, expiry), then the code viewer with a fixed line-number gutter, then a ruled action bar. Mobile collapses the 12 columns to 4 with the metadata cells stacking full width and the textarea filling the viewport width edge to edge.
Diagrammatic, not photographic. The visual vocabulary is a route map: a small SVG system diagram on the About section showing POST /api/pastes → slug → /p/:id → /p/:id/raw as connected nodes on a grid, plus pictogram icons for copy, raw, download, fullscreen, and search drawn at 1.5px stroke. No stock photography, no 3D renders, no illustration for its own sake. The paste content itself, set in monospace with a ruled gutter, is the largest image on the site.
The public entry is an information poster, not a SaaS hero. The first screen is composed as a visible grid you can see, and it is built entirely from accepted content and controls.
clamp(40px, 7vw, 88px) in Archivo 700, three lines deep, reading PASTE IT. / SHARE THE SLUG. / READ IT RAW. sitting directly on the ink ground with no card behind it.ENDPOINT /api/pastes, RAW /p/:id/raw, EXPIRY NEVER → 30D, VISIBILITY PUBLIC / UNLISTED — each row separated by a hairline, with the yellow LIVE chip on the first row.Paste your text here..., then the three ruled metadata cells and the red Create Paste button flush right.The same red rule re-sweeps once across the screen as the Create Paste success transition, so the poster's structural line becomes the product's confirmation gesture.
Interaction Model: Static Motion Tempo: still Hero Dimensionality: flat
The direction's tempo is still and its hero dimensionality is flat, so the interaction model is copied as Static (direction) — minimal and mechanical, with no easing theatrics, no bounce, and no floating panels.
LIVE chip on the first row; the composer in-frame below with the title input, the monospace textarea showing Paste your text here..., the three ruled metadata cells, and the red Create Paste button flush right.prefers-reduced-motion; the caret loop, the 1.2s viewer fade-in, and the red rule sweep are all suppressed, and every hover-only reveal has a visible static equivalent.NFR-1 — Security (explicit) Escape HTML in content; never execute stored JavaScript; use parameterized queries; validate input; rate limit paste creation; limit paste size; prevent path traversal; use random IDs; protect the API from spam; do not store passwords or sensitive data by default; use HTTPS in production. Paste content must be treated as data, not as executable HTML. Rationale: the source states these as mandatory ("Wajib") for a publicly reachable paste host.
NFR-2 — Expiration enforcement (explicit) The backend must genuinely check expiration on every retrieval path rather than only hiding expired pastes in the frontend. Rationale: the source explicitly requires backend enforcement.
NFR-3 — Visibility semantics (explicit) Unlisted pastes must remain accessible via direct URL while not appearing in listings or search; Public pastes may be found if a search system exists. Rationale: the source defines these semantics as a hard constraint.
NFR-4 — Content fidelity (explicit) Paste content must never be altered when displayed or syntax highlighted; the raw endpoint must return the body only, with no navbar, HTML UI, buttons, footer, CSS, or JSON wrapper. Rationale: the source requires exact reproduction and a clean data line.
NFR-5 — Slug unpredictability (explicit)
IDs/slugs must be unique and hard to guess; sequential IDs such as /p/1, /p/2, /p/3 are not allowed. Rationale: the source states this as a hard constraint.
NFR-6 — Deployment portability (explicit)
Do not hardcode localhost; use an environment variable for the base URL so the system works on a custom domain such as https://paste.example.com. Rationale: the source requires custom-domain support.
NFR-7 — Real persistence (explicit) The site must not be frontend-only; a real backend that stores data is required, and localStorage must not be used as the primary database. Rationale: the source states this as a hard constraint.
NFR-8 — Responsive and mobile-friendly (explicit) The site must be comfortable on Android: the textarea fills the screen width, buttons are not too small, the navbar becomes a mobile menu on small screens, and the paste viewer supports horizontal scroll. Rationale: the source requires mobile comfort.
NFR-9 — Performance and readability of long pastes (explicit) The paste viewer must remain comfortable for reading long text, with line numbers, horizontal and vertical scrolling, search, and fullscreen. Rationale: the source requires a comfortable long-text reader.
NFR-10 — Complete, runnable deliverables (explicit) Deliver complete ready-to-run source code, not partial snippets, covering all seventeen listed areas including localhost instructions, hosting deployment, and custom-domain connection. Rationale: the source explicitly forbids snippets.
NFR-11 — Accessibility of motion and contrast (required_inference)
All motion must stop under prefers-reduced-motion, and the palette must maintain the stated contrast ratios (#F2F0EC on #0E0E0F ≈ 17:1; #E4322B on #0E0E0F ≈ 4.6:1, so red is used only at ≥18px or bold). Rationale: required to make the accepted motion and colour direction usable without harming readability.
Source-specified choices are preserved exactly:
project/ containing server.js, package.json, database/database.sqlite, public/index.html, public/style.css, public/app.js, and README.md.The source permits other frameworks provided the whole system remains easy to run, but the documented stack above is the accepted default and is what the deliverables describe.
Assumptions
Constraints
/p/1, /p/2, /p/3; IDs/slugs must be unique and hard to guess./p/:id and /p/:id/raw must genuinely work.id, slug, title, content, language, visibility, created_at, and expires_at.AbC123 in /p/AbC123./p/:id/raw endpoint that returns only the paste body with Content-Type: text/plain; charset=utf-8 and no UI or wrapper.expires_at and enforced by the backend.https://paste.example.com.No completed page designs yet.
Completed design pages will appear here when they are ready to preview.
No completed page designs yet.
Completed design pages will appear here when they are ready to preview.
No comments yet. Be the first!