[{"data":1,"prerenderedAt":794},["ShallowReactive",2],{"page-en-\u002Fprivacy":3,"backlinks-en-\u002Fprivacy":793},{"doc":4,"isFallback":785},{"id":5,"title":6,"body":7,"description":784,"draft":785,"extension":786,"meta":787,"navigation":788,"path":789,"seo":790,"stem":791,"__hash__":792},"content_en\u002Fprivacy.md","Privacy Policy — byteflavour.dev",{"type":8,"value":9,"toc":757},"minimark",[10,15,19,22,27,30,76,83,86,90,98,102,109,113,120,157,160,164,173,196,200,206,211,214,226,230,233,247,254,258,261,265,276,280,284,290,293,297,311,314,339,348,361,370,373,398,402,405,409,414,426,429,441,444,448,451,479,482,489,492,503,516,522,526,529,614,617,620,682,688,694,698,701,745,751,754],[11,12,14],"h1",{"id":13},"privacy-policy","Privacy Policy",[16,17,18],"p",{},"Last updated: 2026-09-09",[16,20,21],{},"This policy describes what data is processed when you visit byteflavour.dev and when you use the git forge at git.byteflavour.dev, and why.",[23,24,26],"h2",{"id":25},"scope","Scope",[16,28,29],{},"This policy covers:",[31,32,33,47,60,66],"ul",{},[34,35,36,40,41,46],"li",{},[37,38,39],"strong",{},"byteflavour.dev"," — this site: the blog and the ",[42,43,45],"a",{"href":44},"\u002Fgarden","Garden",". Served statically, no accounts, no sign-in.",[34,48,49,52,53,59],{},[37,50,51],{},"git.byteflavour.dev"," — a self-hosted ",[42,54,58],{"href":55,"rel":56},"https:\u002F\u002Fforgejo.org\u002F",[57],"nofollow","Forgejo"," instance (a git forge) with user accounts.",[34,61,62,65],{},[37,63,64],{},"sso.byteflavour.dev"," — the sign-in service that authenticates forge accounts.",[34,67,68,71,72,75],{},[37,69,70],{},"count.byteflavour.dev"," — the pageview-counting endpoint. Embedded on byteflavour.dev only, ",[37,73,74],{},"not"," on the forge.",[16,77,78,79,82],{},"The identity provider at ",[80,81,64],"code",{}," also authenticates other self-hosted services that this policy does not cover; those have their own disclosures.",[16,84,85],{},"All four run on the same machine, under the same controller. The difference between the blog and the forge is still substantial: the page you are reading processes almost nothing; a forge with accounts necessarily processes more. Where the two differ, it is stated in the relevant section.",[23,87,89],{"id":88},"data-controller","Data controller",[16,91,92,93,97],{},"The party responsible for all of the above is listed on the ",[42,94,96],{"href":95},"\u002Fimprint","Imprint"," page. Privacy enquiries — including the data subject rights listed below — go to the email address given there. No data protection officer has been appointed; the thresholds in Art. 37 GDPR and § 38 BDSG are not met here.",[23,99,101],{"id":100},"hosting","Hosting",[16,103,104,105,108],{},"All of the services above run on a single server operated by ",[37,106,107],{},"Hetzner Online GmbH",", in their Falkenstein\u002FVogtland data centre in Germany. Hetzner provides the machine; they do not access content as part of normal operation.",[23,110,112],{"id":111},"server-log-files","Server log files",[16,114,115,116,119],{},"Like effectively any web server, the reverse proxy in front of all of the above services logs basic technical information for every request: IP address, timestamp, requested path, HTTP status, referrer, and user-agent string. This applies to the blog ",[37,117,118],{},"and"," to the forge.",[31,121,122,128,134,140,151],{},[34,123,124,127],{},[37,125,126],{},"Purpose",": operation, troubleshooting, and protection against abuse.",[34,129,130,133],{},[37,131,132],{},"Legal basis",": Art. 6(1)(f) GDPR — legitimate interest in operating the services securely and reliably.",[34,135,136,139],{},[37,137,138],{},"Retention",": for analysis, these entries are copied into a log store and discarded there automatically after 30 days. The files on the machine itself are rotated by size rather than by time: the web server's access logs are overwritten after roughly a week in normal operation, and the operating system's logs are capped at 500 MB. How far back those files actually reach therefore depends on volume — I deliberately do not state a fixed deletion deadline for them rather than promise one the setup does not keep.",[34,141,142,145,146,150],{},[37,143,144],{},"In backups",": the web server's access logs are excluded from the backup; they are written only to their own files, which are not backed up. The operating system's logs are part of the backups — and those are where SSH access is recorded, among other things. For them, what the ",[42,147,149],{"href":148},"#backups","Backups"," section says therefore applies in addition.",[34,152,153,156],{},[37,154,155],{},"Access",": not public, not exported to any third party.",[16,158,159],{},"Git access over SSH is logged by the operating system in the same way (time, IP address, fingerprint of the key used). What is said above about the operating system's logs applies to those entries, backups included.",[23,161,163],{"id":162},"intrusion-detection","Intrusion detection",[16,165,166,167,172],{},"An intrusion detection layer (",[42,168,171],{"href":169,"rel":170},"https:\u002F\u002Fwww.crowdsec.net\u002F",[57],"CrowdSec",") reads those same access logs and temporarily blocks suspicious IP addresses at the network level — for instance after repeated failed sign-in attempts. What it processes is the IP address, along with the reason for the block and when it expires. These decisions are kept locally on the machine and expire on their own.",[31,174,175,180,190],{},[34,176,177,179],{},[37,178,132],{},": Art. 6(1)(f) GDPR — legitimate interest in defending against attacks.",[34,181,182,185,186,189],{},[37,183,184],{},"Shared with a third party",": this instance is enrolled in the community network run by ",[37,187,188],{},"CrowdSec SAS"," (France). That cuts both ways: it pulls blocklists of known attacker addresses from there, and it reports its own findings back. What is transmitted is the flagged IP address together with the detection scenario it triggered and a timestamp — not the content of your requests, not your account, nothing else. This only affects addresses that actually triggered a detection scenario; the IP address of an ordinary visit is not reported.",[34,191,192,195],{},[37,193,194],{},"Location of processing",": France, so within the EU.",[23,197,199],{"id":198},"the-forge-accounts-and-content","The forge: accounts and content",[16,201,202,203,205],{},"This section applies to ",[37,204,51],{}," only. Reading the forge without signing in leaves nothing beyond the server logs described above.",[207,208,210],"h3",{"id":209},"account-data","Account data",[16,212,213],{},"An account on the forge stores: username, email address, display name, the time of registration and of the most recent sign-in, and an identifier for the authentication method the account uses. On top of that, whatever you add yourself: uploaded SSH and GPG keys, generated API tokens, avatar and profile details, and your settings (notification preferences, for example).",[31,215,216,221],{},[34,217,218,220],{},[37,219,126],{},": running the account and attributing your contributions.",[34,222,223,225],{},[37,224,132],{},": Art. 6(1)(b) GDPR — performance of the usage relationship you enter into by registering.",[207,227,229],{"id":228},"content","Content",[16,231,232],{},"Repositories, commits, issues, pull requests, comments, reactions, and attachments are stored for as long as they exist. Two things are easy to overlook about a git forge:",[31,234,235,241],{},[34,236,237,240],{},[37,238,239],{},"Public repositories are public."," Content in a repository marked public is readable by anyone on the internet without signing in — including your username and your contributions. That is the point of the thing, but it is a publication.",[34,242,243,246],{},[37,244,245],{},"Commits carry whatever you put in them."," The name and email address in a commit come from your local git configuration, not from your account here. They are taken over verbatim and become part of the history. Changing git history after the fact requires rewriting it and force-pushing — that is true technically, regardless of what permissions you or I hold.",[16,248,249,250,253],{},"If you would rather avoid that: git lets you set ",[80,251,252],{},"user.email"," to an address that is not your real one.",[207,255,257],{"id":256},"session-cookies","Session cookies",[16,259,260],{},"The forge sets technically necessary cookies: a session identifier after sign-in, a token protecting against cross-site request forgery, and — if you choose it — a cookie for staying signed in. These are required for signed-in operation and are exempt from consent under § 25(2) TTDSG. They are not tracking cookies, and no third-party cookies are set.",[207,262,264],{"id":263},"email-notifications","Email notifications",[16,266,267,268,271,272,275],{},"The forge sends notifications (about issues, pull requests, or security events, for example) to the address on your account. Delivery goes through a self-hosted mail server on the same machine; the sender is ",[80,269,270],{},"forgejo@byteflavour.dev",". ",[37,273,274],{},"No"," external email provider is involved. What your own email provider does with the delivered message is outside my control. Notifications can be switched off in your account settings.",[23,277,279],{"id":278},"signing-in","Signing in",[207,281,283],{"id":282},"signing-in-via-ssobyteflavourdev","Signing in via sso.byteflavour.dev",[16,285,286,287,289],{},"The forge has no password registration of its own. Sign-in goes through a self-hosted identity provider at ",[80,288,64],{},", running on the same machine under the same controller. It processes username, email address, credentials (password hash and, where configured, a second factor), and sign-in logs. After a successful sign-in it passes on to the forge only what is needed to create and match the account: identifier, username, display name, and email address.",[16,291,292],{},"There is no local password registration; accounts are created through the identity provider or by an administrator. The external sign-in routes described below add the ability to register yourself using an account you already hold with another provider.",[207,294,296],{"id":295},"signing-in-with-an-external-identity-provider","Signing in with an external identity provider",[16,298,299,300,303,304,307,308,310],{},"You can also sign in with an account you already hold at ",[37,301,302],{},"GitHub"," or ",[37,305,306],{},"Codeberg",". What is actually offered on the sign-in page at ",[80,309,64],{}," is what counts. The transfer described here happens only if you use one of those routes — it is tied to your choice, not to the mere presence of the button.",[16,312,313],{},"What happens if you sign in through one of these providers:",[315,316,317,323,328,331,334],"ol",{},[34,318,319,320,322],{},"You pick the provider at ",[80,321,64],{},".",[34,324,78,325,327],{},[80,326,64],{}," redirects your browser to that provider and acts as the requesting application towards it.",[34,329,330],{},"You sign in at that provider, which asks you to consent to sharing your details.",[34,332,333],{},"The provider returns your identifier, your username, and — depending on the provider and on what you release — your email address and display name.",[34,335,336,337,322],{},"Those details are used to create or match your account at ",[80,338,64],{},[16,340,341,344,345,347],{},[37,342,343],{},"Account creation and linking:"," the first sign-in through an external provider automatically creates an account at ",[80,346,64],{},". If the email address reported by that provider matches one on an account that already exists, the two are linked rather than a second account being created — the external route then becomes an additional way into your existing account. There is still no registration with a local password.",[16,349,350,353,354,356,357,322],{},[37,351,352],{},"An account is not yet access:"," a successful sign-in creates an account with the identity provider only. Whether it lets you use any particular service reachable through it depends on that service — some require a separate authorisation on top. Without it, no data is passed to the service in question. Your account, along with the details taken from the provider, still exists at ",[80,355,64],{}," regardless — including in the case where you end up able to use no service at all. You can ask for it to be deleted at any time; see ",[42,358,360],{"href":359},"#retention-and-deletion","Retention and deletion",[16,362,363,364,366,367,369],{},"One distinction that matters for classifying this: the transfer to the external provider happens at the identity-provider layer, at ",[80,365,64],{},", not by the forge itself — the forge only ever talks to ",[80,368,64],{}," and does not learn which external provider you used, beyond an identifier for the sign-in route. The controller is the same person in both cases; technically they are two different participants.",[16,371,372],{},"What reaches a third party: redirecting means your browser connects to that provider, which therefore learns your IP address, your user agent, and the fact that you are trying to sign in to this instance. The account details listed above are then received back from them.",[31,374,375,380,386,392],{},[34,376,377,379],{},[37,378,132],{},": Art. 6(1)(b) GDPR — the processing happens only if you actively choose this route, and is then necessary to create the account.",[34,381,382,385],{},[37,383,384],{},"Transfers outside the EU",": GitHub is based in and processes in the United States, so signing in through GitHub means a transfer to a third country within the meaning of Chapter V GDPR. Codeberg is run by a registered association based in Germany; no third-country transfer occurs there. If you would rather avoid one, Codeberg is the route without it.",[34,387,388,391],{},[37,389,390],{},"Avoidable",": these routes are alternatives, not requirements. If you would rather not use them, don't — the transfer then does not happen.",[34,393,394,397],{},[37,395,396],{},"The providers' own policies",": what they do on their side is governed by their own privacy policies, over which I have no influence.",[23,399,401],{"id":400},"no-third-party-fonts","No third-party fonts",[16,403,404],{},"Typefaces are self-hosted, not loaded from Google Fonts or any other third-party font service. Loading fonts from a third party would send your IP address to that party on every page view — this site avoids that by design. That holds for the blog and for the forge alike.",[23,406,408],{"id":407},"pageview-stats-goatcounter","Pageview stats (GoatCounter)",[16,410,411],{},[37,412,413],{},"On byteflavour.dev only — the forge has no pageview analytics embedded at all.",[16,415,416,417,422,423,425],{},"This site measures aggregate pageviews through a self-hosted ",[42,418,421],{"href":419,"rel":420},"https:\u002F\u002Fwww.goatcounter.com\u002F",[57],"GoatCounter"," instance (reachable at ",[80,424,70],{},"). No cookie is set. To form a unique \"visit\" without a persistent identifier, GoatCounter briefly processes your IP address and User-Agent into a daily-rotating, salted hash — neither that hash nor the raw values (IP address, full User-Agent) are stored persistently.",[16,427,428],{},"What is stored, aggregated: the page viewed, referrer, coarse browser\u002FOS type, screen-size category, and coarse country of origin — never attributable to an individual visitor.",[16,430,431,432,436,437,322],{},"This data is never shared with or sold to any third party. The aggregate numbers are publicly visible on the ",[42,433,435],{"href":434},"\u002Fstats","Stats page",", alongside a link to the technical interface schema (OpenAPI) that shows what this gateway exposes publicly (not to be confused with what GoatCounter itself processes on the backend — described above). The reasoning behind choosing GoatCounter is in this ",[42,438,440],{"href":439},"\u002Fgarden\u002Fweb-analytics","garden note",[16,442,443],{},"There is no advertising and no other analytics tools or third-party trackers, on this site or on the forge.",[23,445,447],{"id":446},"cookies-on-this-site","Cookies on this site",[16,449,450],{},"The blog sets exactly one cookie:",[31,452,453,462,467,473],{},[34,454,455,458,459],{},[37,456,457],{},"Name",": ",[80,460,461],{},"locale_pref",[34,463,464,466],{},[37,465,126],{},": remembers whether you're viewing the site in English or German, so you don't get redirected to a different language on your next visit.",[34,468,469,472],{},[37,470,471],{},"Duration",": 1 year.",[34,474,475,478],{},[37,476,477],{},"Third parties",": none. This cookie is never read by, or shared with, anyone but this site.",[16,480,481],{},"Under German law (TTDSG §25(2)) and the EU ePrivacy Directive, cookies that are technically necessary for a service you explicitly use — like remembering a language choice — don't require opt-in consent, unlike tracking or advertising cookies. That's the category this cookie falls into. It's set with a brief on-screen notice the first time, not silently, and not gated behind an \"accept\" click, since there's nothing optional to accept.",[16,483,484,485,488],{},"The forge's cookies are described under ",[42,486,257],{"href":487},"#session-cookies"," above.",[23,490,149],{"id":491},"backups",[16,493,494,495,498,499,502],{},"The machine is backed up nightly, encrypted, to ",[37,496,497],{},"Scaleway"," object storage in their Paris region (",[80,500,501],{},"fr-par","), France. The backup includes the databases of the forge and of the identity provider — that is, account data and content. The data is encrypted before it leaves the machine; the key is not held by the provider, who therefore cannot read the backups.",[31,504,505,510],{},[34,506,507,509],{},[37,508,132],{},": Art. 6(1)(f) GDPR — legitimate interest in being able to recover from a failure.",[34,511,512,515],{},[37,513,514],{},"Location",": France, so within the EU. No third-country transfer.",[16,517,518,521],{},[37,519,520],{},"One limitation I would rather state than gloss over:"," the backups sit in storage that technically prevents modification and deletion for at least 90 days — which is exactly the protection against ransomware and against accidental deletion, and it works against me too. In practice: if you ask for deletion, your account is deleted from the live systems, but the data remains in backups already written until that period expires, and then ages out. During that time it is not used, not searched, and not read at all except in an actual restore. See the deletion section below.",[23,523,525],{"id":524},"recipients","Recipients",[16,527,528],{},"In summary — no third parties beyond these are involved:",[530,531,532,550],"table",{},[533,534,535],"thead",{},[536,537,538,542,545,548],"tr",{},[539,540,541],"th",{},"Recipient",[539,543,544],{},"What for",[539,546,547],{},"What goes there",[539,549,514],{},[551,552,553,566,579,591,604],"tbody",{},[536,554,555,558,560,563],{},[556,557,107],"td",{},[556,559,100],{},[556,561,562],{},"runs the machine; no content access in normal operation",[556,564,565],{},"Germany",[536,567,568,571,573,576],{},[556,569,570],{},"Scaleway SAS",[556,572,491],{},[556,574,575],{},"encrypted backups, key stays here",[556,577,578],{},"France",[536,580,581,583,586,589],{},[556,582,188],{},[556,584,585],{},"intrusion detection",[556,587,588],{},"IP addresses that triggered a detection scenario",[556,590,578],{},[536,592,593,595,598,601],{},[556,594,302],{},[556,596,597],{},"sign-in",[556,599,600],{},"only if you use that sign-in route — see above",[556,602,603],{},"USA",[536,605,606,608,610,612],{},[556,607,306],{},[556,609,597],{},[556,611,600],{},[556,613,565],{},[16,615,616],{},"There are no ad networks, no analytics vendors, no content delivery networks, and no sale of data.",[23,618,360],{"id":619},"retention-and-deletion",[530,621,622,632],{},[533,623,624],{},[536,625,626,629],{},[539,627,628],{},"Data",[539,630,631],{},"Retained",[551,633,634,642,650,658,666,674],{},[536,635,636,639],{},[556,637,638],{},"Server log files (incl. IP address)",[556,640,641],{},"30 days in the log store; files on the machine rotate by size — see above",[536,643,644,647],{},[556,645,646],{},"Intrusion-detection blocking decisions",[556,648,649],{},"until the block expires",[536,651,652,655],{},[556,653,654],{},"Account data (forge and sign-in)",[556,656,657],{},"until the account is deleted",[536,659,660,663],{},[556,661,662],{},"Contributions in public repositories",[556,664,665],{},"see below",[536,667,668,671],{},[556,669,670],{},"Aggregate pageview stats",[556,672,673],{},"indefinitely, but with no personal reference",[536,675,676,679],{},[556,677,678],{},"Encrypted backups",[556,680,681],{},"at least 90 days, see above",[16,683,684,685,687],{},"An account on the forge can be deleted by you in your account settings, provided it has a password of its own on the forge — the deletion form asks for that password as confirmation. Accounts created through the identity provider have no such password; for those, the route is an informal message to the address on the ",[42,686,96],{"href":95}," page. The same goes for anyone else who would rather do it that way. Deletion removes account data, keys, tokens, and settings.",[16,689,690,691,693],{},"What deleting an account does ",[37,692,74],{}," automatically remove: contributions that have become part of a shared project history — commits in a repository's history, and comments on issues or pull requests that others have replied to. On account deletion these are detached from the account and reassigned to an anonymous placeholder, but they remain in place; commits keep the authorship details written into them. If you want specific content removed beyond that, write to me — it is possible, but it is a manual intervention, and for git history it may mean a rewrite that does not reach other copies of the repository.",[23,695,697],{"id":696},"your-rights","Your rights",[16,699,700],{},"Under the GDPR, you have the right to:",[31,702,703,709,715,721,727,733,739],{},[34,704,705,708],{},[37,706,707],{},"access"," the personal data processed about you (Art. 15),",[34,710,711,714],{},[37,712,713],{},"rectification"," of inaccurate data (Art. 16),",[34,716,717,720],{},[37,718,719],{},"erasure"," of your data (Art. 17),",[34,722,723,726],{},[37,724,725],{},"restriction"," of processing (Art. 18),",[34,728,729,732],{},[37,730,731],{},"data portability"," — receiving your data in a common format (Art. 20),",[34,734,735,738],{},[37,736,737],{},"object"," to processing based on legitimate interest (Art. 21), and",[34,740,741,744],{},[37,742,743],{},"lodge a complaint"," with a data protection supervisory authority (Art. 77).",[16,746,747,748,750],{},"An informal message to the address on the ",[42,749,96],{"href":95}," page is enough. To avoid handing data to the wrong person, I may ask you to confirm an access or deletion request about an account from the email address registered on it.",[16,752,753],{},"The competent supervisory authority, following the controller's location, is the Data Protection Commissioner of the State of Brandenburg (Die Landesbeauftragte für den Datenschutz und für das Recht auf Akteneinsicht Brandenburg). You are equally free to contact the authority where you live.",[16,755,756],{},"For the blog, these rights have little to act on — one preference cookie and standard server logs is all there is. For the forge they are substantial, and they apply in full either way.",{"title":758,"searchDepth":759,"depth":759,"links":760},"",2,[761,762,763,764,765,766,773,777,778,779,780,781,782,783],{"id":25,"depth":759,"text":26},{"id":88,"depth":759,"text":89},{"id":100,"depth":759,"text":101},{"id":111,"depth":759,"text":112},{"id":162,"depth":759,"text":163},{"id":198,"depth":759,"text":199,"children":767},[768,770,771,772],{"id":209,"depth":769,"text":210},3,{"id":228,"depth":769,"text":229},{"id":256,"depth":769,"text":257},{"id":263,"depth":769,"text":264},{"id":278,"depth":759,"text":279,"children":774},[775,776],{"id":282,"depth":769,"text":283},{"id":295,"depth":769,"text":296},{"id":400,"depth":759,"text":401},{"id":407,"depth":759,"text":408},{"id":446,"depth":759,"text":447},{"id":491,"depth":759,"text":149},{"id":524,"depth":759,"text":525},{"id":619,"depth":759,"text":360},{"id":696,"depth":759,"text":697},"What data this site and the forge at git.byteflavour.dev process, and why.",false,"md",{},true,"\u002Fprivacy",{"title":6,"description":784},"privacy","TJVBKimz1FZDv7BcfUvZoBH0i6NgnTQ0XbkmKHF6a_4",[],1789414681699]