1337.kiwi

flatlink. Short links you own.

A short-link service with a QR generator that you run yourself. Plain PHP – no database server, no Composer, no build step. Copy it, configure it, done.

View on GitHub Get started Deutsch

The flatlink link manager: a list of short codes, names, tags, groups and click counts, with a tag cloud above for filtering.
The link manager of your own instance – tags for filtering, work groups, click counts.

Why open?

“We don’t track you” is just a claim without the source

Any short-link service can claim it doesn’t track its visitors. The claim only becomes verifiable once the source code sits next to it. This is everything flatlink stores about a short link:

No record of individual visits – hence no IP addresses, no times of day, no stored user agents. The lower three lines are totals: three coarse attributes are taken from each request and added up immediately, and of the referrer only the hostname survives (never the path, which can carry a search query). That answers “where do my clicks come from” without holding on to a single visit – and if even that is too much, one line of configuration turns it off.

The redirect path doesn’t even start a session unless the link is password-protected. Checking is explicitly invited – that is what the open source is for: the entire counting logic lives in inc/store.php.

A look inside

What it feels like

Statistics for a short link: total count, the last 30 days as bars, a monthly overview and a CSV export button.
Statistics. Bars per day, totals per month, CSV export. The data holds no more than that – and it is not supposed to.
The QR designer with selectors for module and eye shape, colour pickers, frame and logo settings and a live preview of the code.
QR designer. Shapes, colours, a logo in the middle, a frame with text. As SVG, PNG and print-ready PDF – from its own encoder.
The QR series page: format, colour and shape selection, below it a list of links with checkboxes.
QR batches. Twenty codes in a single ZIP, with a CSV index for the print shop.
The form for a new short link with fields for target address, name, tags and a collapsed section for campaign parameters.
Creating. Custom name, tags, expiry date, password protection and a builder for campaign parameters.
A link-in-bio page on a dark background with five light buttons stacked vertically.
Link-in-Bio. One page with several targets under a single short code. Counted like everything else: per day, without a visitor record.

The screenshots show the German interface; the instance language can be set to English.

Measured

Fifty million links cost three percent

A file instead of a database server sounds like a limit. Measured against fifty million short links (a 13 GB file), throughout with the 128 MB memory limit typical of shared hosting:

Operation 2 links 50 M links
Redirect, counted0.208 ms0.214 ms
First page of the admin list–0.04 ms
Last page (page 1,000,000)–0.74 ms
One pass over every link–50 s at 2 MB

A short link is found by its primary key – whether two or fifty million others sit next to it changes nothing. Anything that grows with the data is streamed rather than loaded: cleanup, search and export go row by row at constant memory.

Honestly alongside: writes happen one after another, not in parallel – SQLite takes one writer. And on shared hosting the ceiling is eventually your contract’s inode quota, not the database. Both are spelled out with numbers in the README.

What you get

A complete service, not a code snippet

QR codes without third-party code

Its own encoder per ISO/IEC 18004. Shapes, colours, a logo in the middle, a frame with free text – as SVG, PNG and print-ready PDF.

Printed, and still changeable

You can’t recall a printed code – but you can change where it points. Every change is recorded, the last twenty per link.

One code, several targets

Link-in-bio pages with their own styling, plus switches by language or device and time windows per link.

Browser extension

Shorten the page you’re on with one click – against your own instance, for Chrome and Firefox.

Wi-Fi, contact, event

Static QR codes whose content sits inside the code itself: they keep working even if the instance is gone.

For organisations

Sign-in against an existing directory (LDAP, Active Directory) or via the web server (Shibboleth, SAML, OIDC).

Order once you pass a hundred links

Tags, work groups with their own permissions and limits, namespaces per department, bulk CSV import.

Several domains

One flatlink server runs any number of addresses, each with its own namespace: client-a.link/shop and client-b.link/shop are two different short links. Clients bring their own domain without having to coordinate codes.

Passkeys instead of codes

Two-step sign-in: a passkey replaces the password, a one-time code adds to it.

API and moving in

A REST API with an OpenAPI description and scoped keys. Exports from Bitly, YOURLS, Shlink and Kutt can be imported as they are.

No database to set up

Links and accounts live in one SQLite file. No server for anyone to set up and maintain, no migration steps on update – and a backup is a file copy.

Abuse protection

Rate limits, a report form, blocking and optionally Google Safe Browsing – including a re-check pass over existing links.

Access and erasure

Every account downloads its data as JSON and deletes itself – without asking the operator.

Sessions and audit log

Sign out individual devices; administrative actions stay traceable – and visitors never appear in that log.

Accessible and bilingual

Interface in German or English, a WCAG 2.1 AA self-assessment and a template declaration for public bodies.

Who it’s for

People who’d rather run it themselves

Clubs, surgeries, restaurants

Print a QR code and change its target later without replacing the sticker. That is what a short link on paper is for.

Libraries, schools, public bodies

Short links that must not leave the building. Sign-in via your existing directory, groups per department, your own namespaces.

Agencies

Several brands under one roof: a domain per client, shared work groups, an API for automation.

Two instances run in public: 1337.kiwi as a free service and hfmt.art at the Hamburg University of Music and Drama, whose requirements shaped a good deal of what flatlink does – directory sign-in, groups, namespaces, the CSV import from YOURLS.

Installation

Copy, configure, done

No Composer, no build step, no database server. All you need is PHP 8.1 (with pdo_sqlite, which is practically always there) and a web server that can rewrite paths.

$ git clone https://github.com/HerrBarmann/flatlink.git $ cd flatlink $ cp inc/config.example.php inc/config.php # adjust, done

If you prefer containers: a Dockerfile and a compose file are in the repository, plus a Kubernetes manifest. The container runs rootless and with any user ID.

For serious operation there is a thorough deployment guide – from file permissions through mail delivery to a complete Shibboleth setup – and a customisation guide for your own colours and logos.

Licence

Free to use, with two conditions

flatlink is licensed under the GNU AGPL v3 with an additional attribution term under § 7(b) of the licence. Use it, run it, change it, redistribute it, rename it, recolour it – all allowed, commercially too, without asking. Two things stay:

The attribution line stays

Every interface names “flatlink” and links to this project. Translate it, reword it, set it small and quiet – all fine. Hiding it is not.

Changes stay open

Anyone offering a modified version as a service makes its source available to their users. Running it unmodified publishes nothing.

Why not MIT: because MIT allows closing the source and building a service from it where nobody can check what happens to the click data. Being able to check exactly that is the point of this project.

For a version without the attribution line – as a white label, say – a written waiver is available. A short email is enough.

Contributing

Bug reports and pull requests are welcome – with one request up front: being dependency-free is not an accident, it is the core of the project. A patch that requires Composer, a build step or a database server will not be merged, however much more elegant it would be.

Please don’t report security issues publicly – use the route described in SECURITY.en.md.

To the repository Report a bug