feat(projects): rewrite project texts with verified facts
Old copy was generic and did not match the code. Texts now describe the real features: PlayStation sync, file imports, tests, security. Add per-game itch.io links, new chips, and a test that checks every About project key exists in en and de.
This commit is contained in:
1 parent
106d39453d
commit
8681508cb6
5 files changed
+250
-185
No files matched your search
+92
-84
@@ -187,13 +187,13 @@
|
||||
"PROJECT": {
|
||||
"TBP": {
|
||||
"TITLE": "To Be Played",
|
||||
"DESCRIPTION": "A Steam backlog tracker that imports my library, keeps status, rating and notes, and suggests what to play next for the time I have. Three self-hosted services: auth, core and a React frontend.",
|
||||
"DESCRIPTION": "A backlog tracker for Steam and PlayStation that syncs my libraries, keeps status, rating and notes, and suggests what to play next for the time I have. Three self-hosted services: auth, core and a React frontend.",
|
||||
"LINK_EXTERNAL": "tbp.andreas-dahm.eu",
|
||||
"LINK_INTERNAL": "Project details",
|
||||
"HIGHLIGHTS": {
|
||||
"P1": "Steam sync of games, playtime, achievements and wishlist, plus time to beat from IGDB.",
|
||||
"P2": "\"What to play next\" suggestions filtered by time budget and mood.",
|
||||
"P3": "Auth service signs the tokens with Ed25519, Core only holds the public key."
|
||||
"P1": "Steam and PlayStation sync of games, playtime, achievements and trophies, plus time to beat from IGDB.",
|
||||
"P2": "\"What to play next\": seven rows of suggestions, filtered by a time budget; a mood filter for the random picks.",
|
||||
"P3": "Auth signs the tokens with Ed25519; Core fetches the public key by key id and only verifies."
|
||||
}
|
||||
},
|
||||
"CLAUDE_BOX": {
|
||||
@@ -208,55 +208,55 @@
|
||||
},
|
||||
"P2": {
|
||||
"TITLE": "Playground",
|
||||
"DESCRIPTION": "This project is, as the name suggests, a personal playground that will grow over time. It showcases various private projects I’m working on and documents their progress, key ideas, and milestones.",
|
||||
"DESCRIPTION": "This site. A portfolio and a place to try algorithms in the browser: pathfinding, sorting, fractals in GLSL, and a cloth and a double pendulum simulation as WebGPU compute shaders. Angular, German and English, built and shipped by my own CI.",
|
||||
"LINK_EXTERNAL": "andreas-dahm.eu",
|
||||
"LINK_INTERNAL": "Project details",
|
||||
"HIGHLIGHTS": {
|
||||
"P1": "Modern frontend stack and a CI/CD pipeline on Gitea Actions (Angular, Docker).",
|
||||
"P2": "Showcasing personal projects and improving algorithmic skills over time.",
|
||||
"P3": "Deepening knowledge in JavaScript/TypeScript, Angular and related technologies through hands-on practice."
|
||||
"P1": "Visualizations from Dijkstra to raymarched 3D fractals, all on the client.",
|
||||
"P2": "Two WebGPU compute simulations (cloth with XPBD, double pendulum) with a CPU or WebGL fallback.",
|
||||
"P3": "Gitea Actions pipeline: lint, i18n check, tests, Lighthouse, Docker image to my own registry."
|
||||
}
|
||||
},
|
||||
"P1": {
|
||||
"TITLE": "El Mucho",
|
||||
"DESCRIPTION": "This is my first published game on Steam. I developed it together with my brother, fulfilling one of my personal goals: creating and releasing my own video game.",
|
||||
"DESCRIPTION": "My first published game, a turn-based tactical RPG, made with my brother over about three years and released on Steam in 2022. He designed it, I built it.",
|
||||
"LINK_EXTERNAL": "Steam Store",
|
||||
"LINK_INTERNAL": "Project details",
|
||||
"HIGHLIGHTS": {
|
||||
"P1": "Publishing a game on Steam and integrating the Steam API.",
|
||||
"P2": "Designing, planning and developing a complete game from scratch.",
|
||||
"P3": "Implementing complex algorithms, including a custom A* pathfinding system and game AI logic."
|
||||
"P1": "All the code in Unity (C#), the builds, the Steam setup and the release.",
|
||||
"P2": "16 missions, achievements, three languages (EN, DE, ES).",
|
||||
"P3": "A custom AI for the enemy units and my own A* pathfinding on the map grid."
|
||||
}
|
||||
},
|
||||
"P0": {
|
||||
"TITLE": "Game Jams",
|
||||
"DESCRIPTION": "This section showcases several of my past game jam creations. Game jams are a great way to explore new game ideas and quickly validate whether a game loop works. The tight time constraints force you to focus, and it’s always exciting to see what can be achieved within such a short timeframe.",
|
||||
"DESCRIPTION": "Three 48-hour jam games from the BeansJam series, in teams of two or three. I was the programmer and lead each time. All of them are playable on itch.io.",
|
||||
"LINK_EXTERNAL": "Itch.io Collection",
|
||||
"LINK_INTERNAL": "Project details",
|
||||
"HIGHLIGHTS": {
|
||||
"P1": "Planning a realistic project scope with a team that can be built within 48 hours.",
|
||||
"P2": "Learning to stay focused and work effectively under strict time constraints.",
|
||||
"P3": "Experiencing the joy of creating a playable game in a short timeframe and seeing others enjoy it."
|
||||
"P1": "Peter Blue and the carnival of death (2018), Arkonauts (2020), Sloth Bros (2022).",
|
||||
"P2": "Friday to Sunday: a playable game in 48 hours, every time.",
|
||||
"P3": "A cheap way to find out whether a game idea works before committing years to it."
|
||||
}
|
||||
},
|
||||
"DIPLOMA": {
|
||||
"TITLE": "Diploma thesis - Collision detection and handling of complex garments.",
|
||||
"DESCRIPTION": "The thesis deals with the detection and handling of collisions between and within individual items of clothing in real time. This is particularly challenging due to the flexibility of fabrics and their varying properties.",
|
||||
"DESCRIPTION": "Self-intersections inside one garment part, such as a pleated skirt, in the cloth simulation Vidya at assyst. I adapted Intersection Contour Minimization to Vidya's k-DOP hierarchy and extended the response with mass and velocity. FH Bingen, March 2010.",
|
||||
"LINK_INTERNAL": "Project details",
|
||||
"HIGHLIGHTS": {
|
||||
"P1": "Real-time handling of collision detection and response.",
|
||||
"P2": "Understanding and evaluating scientific papers.",
|
||||
"P3": "Adaptation and further development of previous research work."
|
||||
"P1": "Detection on the existing BVH with k-DOPs; one shared hierarchy cut update and traversal time in half.",
|
||||
"P2": "Compared four published methods and took the global Intersection Contour Minimization variant.",
|
||||
"P3": "About 20 % faster than the existing self-collision detector plus Layering, planned for productive use."
|
||||
}
|
||||
},
|
||||
"TRIBBLE": {
|
||||
"TITLE": "Homeserver 'Tribble'",
|
||||
"DESCRIPTION": "This project is about setting up and maintaining my own homeserver. It runs several Docker containers like Gitea, Jellyfin and more. It's a great learning experience in self-hosting and system administration.",
|
||||
"DESCRIPTION": "A mini PC at home runs Gitea with its Actions runner and registry, plus Jellyfin, Paperless-ngx and AdGuard Home. A VPS serves the public sites and pulls new images over Tailscale with Watchtower. Running since May 2024, backups to Google Drive.",
|
||||
"LINK_INTERNAL": "Project details",
|
||||
"HIGHLIGHTS": {
|
||||
"P1": "Self-hosting of various services using Docker.",
|
||||
"P2": "CI/CD pipeline for the personal website using Gitea.",
|
||||
"P3": "Secure remote access with Tailscale and Traefik."
|
||||
"P1": "Gitea CI builds and tests this site and To Be Played at home; Watchtower deploys the images on the VPS.",
|
||||
"P2": "Traefik for TLS and routing, Tailscale between the two machines.",
|
||||
"P3": "The hard parts were DNS and making the setup repeatable, not the services themselves."
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -309,69 +309,75 @@
|
||||
"LEARNINGS": "Learnings & Soft Skills"
|
||||
},
|
||||
"PLAYGROUND": {
|
||||
"TITLE": "Playground Portfolio",
|
||||
"SHORT_DESCRIPTION": "Frontend portfolio with interactive algorithm visualizations.",
|
||||
"INTRODUCTION": "This website serves as a living portfolio and testing ground for modern web technologies. The goal is to clearly represent complex algorithms and mathematical concepts (such as WebGPU simulations or Raymarching) directly in the browser.",
|
||||
"BULLET_1": "Development with Angular and Material Design.",
|
||||
"BULLET_2": "Implementation of performant visualizations (WebGPU, Shader, Canvas).",
|
||||
"BULLET_3": "Automated CI/CD with Gitea Actions and containerization with Docker.",
|
||||
"BULLET_4": "Internationalization (i18n) for global reach.",
|
||||
"CHALLENGE_1": "Optimizing render performance for complex 3D fractals in real-time.",
|
||||
"CHALLENGE_2": "Architecting a scalable and maintainable frontend structure for diverse sub-projects.",
|
||||
"LEARNING_1": "Effective UI/UX design for complex data-driven visualizations.",
|
||||
"LEARNING_2": "Advanced state management and reactive programming in Angular."
|
||||
"TITLE": "Playground",
|
||||
"SHORT_DESCRIPTION": "This site: a portfolio with algorithm visualizations that run in the browser, some of them on the GPU.",
|
||||
"INTRODUCTION": "This site is where I try things out. I wanted to see how far a plain browser gets with the algorithms I like, so every visualization runs on the client: pathfinding, maze generation, sorting, Conway's Game of Life, fractals in GLSL shaders, and two physics simulations that run as compute shaders on WebGPU. The rest is a normal Angular app with German and English texts, a dark mode, and a CI pipeline that builds and tests it and pushes it as a Docker image to my own registry.",
|
||||
"BULLET_1": "Pathfinding with Dijkstra and A*, maze generation with Prim and Kruskal, five sorting algorithms with sound, Conway's Game of Life and a four-color map.",
|
||||
"BULLET_2": "2D fractals (Mandelbrot, Julia, Burning Ship, Newton) in one GLSL shader, and 3D fractals (Mandelbulb, Mandelbox, Julia-Bulb) by raymarching with normals from the distance field.",
|
||||
"BULLET_3": "A double pendulum and a cloth simulation run as compute shaders on WebGPU. Without WebGPU they fall back to the CPU or WebGL and say so in a snackbar.",
|
||||
"BULLET_4": "Angular with Material and Tailwind, signals for state, German and English, and a service worker (PWA).",
|
||||
"BULLET_5": "CI on Gitea Actions: lint, i18n check, tests, production build and a Lighthouse report. On main it builds a Docker image and pushes it to my own registry.",
|
||||
"CHALLENGE_1": "The cloth solver on the GPU. XPBD constraints that share a particle cannot be solved in parallel without a race. I group the constraints by graph coloring into eight phases, so inside one phase no two constraints touch the same particle and each phase runs fully in parallel.",
|
||||
"CHALLENGE_2": "Not every browser has WebGPU. Both simulations have a CPU or WebGL path and a snackbar that says the fallback is active, so the page works everywhere and nobody wonders why it is slower.",
|
||||
"LEARNING_1": "Compute shaders change how you think about an algorithm. The data layout and the order of writes matter more than the math.",
|
||||
"LEARNING_2": "A small CI with a key-parity check and a Lighthouse run catches the boring mistakes, a missing translation or a slow page, before I see them on the live site."
|
||||
},
|
||||
"TRIBBLE": {
|
||||
"TITLE": "Self-Hosted Infrastructure",
|
||||
"SHORT_DESCRIPTION": "Home server for CI and private services, plus a VPS for the public sites.",
|
||||
"INTRODUCTION": "Two machines: the home server \"Tribble\" runs Gitea with its Actions runner and the private services, and a Hostinger VPS serves the public sites. CI builds the images at home, the VPS pulls new images over Tailscale with Watchtower, and Traefik routes the public sites.",
|
||||
"BULLET_1": "Central management via Docker-Compose and Portainer.",
|
||||
"BULLET_2": "Automatic SSL management and reverse proxy with Traefik.",
|
||||
"BULLET_3": "Private version control and CI with Gitea, media streaming with Jellyfin.",
|
||||
"BULLET_4": "Network-wide ad-blocking and DNS control via AdGuard Home.",
|
||||
"CHALLENGE_1": "Configuring secure network layers and firewall rules for remote access.",
|
||||
"CHALLENGE_2": "Automating backups and recovery strategies for containerized data.",
|
||||
"LEARNING_1": "Deep understanding of modern network protocols and IT security.",
|
||||
"LEARNING_2": "Efficient resource management on limited server systems."
|
||||
"INTRODUCTION": "Two machines. At home a mini PC called \"Tribble\" runs Gitea with its Actions runner and container registry, plus the private services. A Hostinger VPS serves the public sites, this one included. A git push builds the image at home, the VPS pulls it over Tailscale with Watchtower, and Traefik does TLS and routing. It has been running since May 2024.",
|
||||
"BULLET_1": "Hardware: a KODLIX GD50 mini PC with an 8-core Intel Core i5, 32 GB RAM and a 512 GB SSD.",
|
||||
"BULLET_2": "Gitea for git, the Actions runner and the container registry. This site and To Be Played are built and tested there.",
|
||||
"BULLET_3": "Private services: Jellyfin for media, Paperless-ngx for documents, AdGuard Home for DNS and ad blocking, Portainer to look at the containers, Traefik in front.",
|
||||
"BULLET_4": "Tailscale links the two machines. The VPS fetches new images from the registry at home over it, and Watchtower swaps the running container.",
|
||||
"BULLET_5": "Backups go to Google Drive.",
|
||||
"CHALLENGE_1": "DNS. Names have to resolve to the right address from inside the house, over Tailscale and from the public internet, and AdGuard Home sits in the middle of it. Getting that right took longer than any single service.",
|
||||
"CHALLENGE_2": "Getting everything to run at the same time, and then making it repeatable. The first setup was a long list of manual steps. I automated it so the server can be rebuilt without me remembering each one.",
|
||||
"LEARNING_1": "Self-hosting is cheap in money and expensive in time. Most of the time goes into the parts nobody sees: DNS, certificates and backups.",
|
||||
"LEARNING_2": "A git push that ends in a running container on the VPS, with no manual step in between, is the one part of this setup I would not give up."
|
||||
},
|
||||
"EL_MUCHO": {
|
||||
"TITLE": "El Mucho (Steam Release)",
|
||||
"SHORT_DESCRIPTION": "Turn-based tactical RPG, published on Steam.",
|
||||
"INTRODUCTION": "A commercial game that my brother and I took from the first idea to the release on Steam: planned, built and sold. A tactical RPG that combines classic gameplay elements with modern systems.",
|
||||
"BULLET_1": "Game development in Unity (C#).",
|
||||
"BULLET_2": "Integration of Steamworks features (Achievements, Cloud Saves).",
|
||||
"BULLET_3": "Development of a custom tactical AI and pathfinding logic.",
|
||||
"BULLET_4": "Management of the entire asset pipeline and sound design.",
|
||||
"CHALLENGE_1": "Implementing a robust turn-based system with complex dependencies.",
|
||||
"CHALLENGE_2": "Performance optimization for a smooth experience across various hardware profiles.",
|
||||
"LEARNING_1": "Perseverance and focus over a multi-year development cycle.",
|
||||
"LEARNING_2": "Marketing and community management for a digital product."
|
||||
"SHORT_DESCRIPTION": "A turn-based tactical RPG that my brother and I released on Steam.",
|
||||
"INTRODUCTION": "El Mucho is a turn-based tactical RPG in a fictional world called Liberika, inspired by Langrisser. My brother and I made it over about three years next to a full-time job and released it on Steam: Early Access in July 2021, full release in February 2022. The split was clear. He had the idea and did the assets, the level design, testing and balancing. I did everything technical: the code in Unity, the builds, the Steam setup and the release.",
|
||||
"BULLET_1": "Unity and C#. One developer for all of the code.",
|
||||
"BULLET_2": "16 missions, achievements and family sharing on Steam, published as Lobo Corp.",
|
||||
"BULLET_3": "English, German and Spanish. The Spanish translation was done by a friend who is a native speaker.",
|
||||
"BULLET_4": "Art and music are partly open source, partly my brother's, partly mine.",
|
||||
"CHALLENGE_1": "Shipping on Steam as a one-person tech team. Builds, store setup, upload and the Early Access phase all went through me. Early Access started in July 2021, the full release followed in February 2022.",
|
||||
"CHALLENGE_2": "Three languages. Every text exists in English, German and Spanish; for Spanish I worked with a native-speaking friend.",
|
||||
"CHALLENGE_3": "The enemy units. I wrote a custom AI for them and my own A* pathfinding on the map grid.",
|
||||
"LEARNING_1": "Finishing takes longer than building. The stretch from Early Access to the release alone took about seven months.",
|
||||
"LEARNING_2": "A clear split of roles kept a two-person team working for three years: he owned the game, I owned the code and the release."
|
||||
},
|
||||
"GAME_JAMS": {
|
||||
"TITLE": "Rapid Prototyping & Game Jams",
|
||||
"SHORT_DESCRIPTION": "Collection of innovative game concepts, created in under 48 hours.",
|
||||
"INTRODUCTION": "Participation in national competitions (e.g. BeansJam). The focus is on creating functional and fun prototypes under extreme time pressure.",
|
||||
"BULLET_1": "Focus on 'Core Game Loop' and fast feedback.",
|
||||
"BULLET_2": "Collaborative development in small, agile teams.",
|
||||
"BULLET_3": "Effective time management and scope control.",
|
||||
"BULLET_4": "Publishing and iteration based on community voting.",
|
||||
"CHALLENGE_1": "Reducing complex ideas to a Minimum Viable Product (MVP) achievable in 48h.",
|
||||
"CHALLENGE_2": "Rapid bug diagnosis and fixing under massive time pressure.",
|
||||
"LEARNING_1": "Radical prioritization of features ('Kill your darlings').",
|
||||
"LEARNING_2": "Effective communication and decision-making under team stress."
|
||||
"TITLE": "Game Jams",
|
||||
"SHORT_DESCRIPTION": "Three small games, each made in 48 hours from Friday to Sunday.",
|
||||
"INTRODUCTION": "A game jam gives you a theme on Friday evening and wants a playable game by Sunday. I have done three so far, all in the BeansJam series, in teams of two or three. I was always the programmer and the lead; the others brought ideas, assets and testing. All three games are on itch.io.",
|
||||
"BULLET_1": "Peter Blue and the carnival of death (2018, BeansJam): my first jam.",
|
||||
"BULLET_2": "Arkonauts (2020, Mini Beans Jam 6): space invaders in space.",
|
||||
"BULLET_3": "Sloth Bros (2022, BeansJam '22): theme \"we have to stay awake\", one of 65 entries.",
|
||||
"BULLET_4": "Always 48 hours, Friday to Sunday, in a team of two or three.",
|
||||
"CHALLENGE_1": "Scope. 48 hours is not much, and with sleep it is less. Deciding on Friday what we will not build was the most important decision of each jam.",
|
||||
"CHALLENGE_2": "Being the only programmer. Everything the team came up with went through my keyboard, so an idea had to be cheap to build or it was dropped.",
|
||||
"LEARNING_1": "A playable loop on Saturday beats a better idea on Sunday.",
|
||||
"LEARNING_2": "A jam is where I find out whether a game idea works before spending years on it. El Mucho took three years; a jam game takes a weekend.",
|
||||
"LINK_COLLECTION": "All games on itch.io"
|
||||
},
|
||||
"DIPLOMA": {
|
||||
"TITLE": "Scientific Diploma Thesis",
|
||||
"SHORT_DESCRIPTION": "Real-time collision detection for complex, flexible 3D objects.",
|
||||
"INTRODUCTION": "Research work in the field of computer graphics. Development of an algorithm for physically correct simulation of fabrics and clothing in real-time.",
|
||||
"BULLET_1": "Mathematical modeling of mass-spring systems.",
|
||||
"BULLET_2": "Low-level programming with Java.",
|
||||
"BULLET_3": "Optimization through spatial data structures (AABB Trees, Bounding Spheres).",
|
||||
"BULLET_4": "Scientific evaluation of simulation precision.",
|
||||
"CHALLENGE_1": "Handling 'self-collisions' in high-resolution meshes without performance loss.",
|
||||
"CHALLENGE_2": "Mathematical stabilization of integration methods under high force impacts.",
|
||||
"LEARNING_1": "Transferring theoretical research into productive, high-performance code.",
|
||||
"LEARNING_2": "Precise working and documentation according to scientific standards."
|
||||
"TITLE": "Diploma Thesis: Cloth Self-Collisions",
|
||||
"SHORT_DESCRIPTION": "Detecting and resolving self-intersections in simulated garments, done at assyst for the cloth simulation Vidya (2010).",
|
||||
"INTRODUCTION": "Simulated cloth folds into itself. A pleated skirt, for example, intersects with its own fabric, and once two parts of one garment have passed through each other a normal collision response cannot pull them apart again. Collisions between different layers were already handled in Vidya; self-intersections inside one garment part were not. In my diploma thesis at assyst I adapted Intersection Contour Minimization (Volino et al. 2006) to Vidya and extended it so the response uses mass and velocity. Written in Java, finished in March 2010 at FH Bingen, and planned for productive use.",
|
||||
"BULLET_1": "Detection runs on Vidya's existing bounding volume hierarchy with k-DOPs. For self-tests the traversal skips the inner-node tests it does not need.",
|
||||
"BULLET_2": "The intersection contour is built from segments. A spatial hash joins them even when floating point errors leave small gaps.",
|
||||
"BULLET_3": "The response is my extension: it uses mass and velocity, so the result is physically plausible instead of a plain geometric push.",
|
||||
"BULLET_4": "The parameter h0 is derived from the mean triangle area, so it needs no manual tuning.",
|
||||
"BULLET_5": "The method shares one BVH with Vidya's existing TriangleSelfCollisionDetector.",
|
||||
"CHALLENGE_1": "Cost. The test scene had about 16,000 particles at about 430 ms per step on a 2 GHz Core 2 Duo. Sharing one BVH between my detector and the existing one cut the update and traversal time in half and the overhead by about 80 %. On top of the existing detection the method adds about 5 %.",
|
||||
"CHALLENGE_2": "Choosing the method. I compared Baraff (2003), Bridson (2002) and Volino (2000, 2006) and took the global variant of Intersection Contour Minimization, because it also resolves intersections that already exist.",
|
||||
"CHALLENGE_3": "Known limits, documented: nearly planar surfaces and a vertex that lies exactly on a triangle plane are not handled.",
|
||||
"LEARNING_1": "Reading papers properly. The same problem had four published answers, and the useful work was understanding where each one fails.",
|
||||
"LEARNING_2": "Measure before you claim. The method alone was about 20 % faster than the existing detector plus Layering, and that number only counts because it comes from the same scene on the same machine."
|
||||
},
|
||||
"CLAUDE_BOX": {
|
||||
"TITLE": "Claude-in-a-Box",
|
||||
@@ -389,17 +395,19 @@
|
||||
},
|
||||
"TBP": {
|
||||
"TITLE": "To Be Played",
|
||||
"SHORT_DESCRIPTION": "A Steam backlog tracker that helps decide what to play next.",
|
||||
"INTRODUCTION": "My Steam library has far more games than I will ever finish. To Be Played imports the library, keeps the backlog with status, rating and notes, and suggests what to play next for the time I have. Behind it are three services: an auth service that signs the login tokens, a core service for the library and the statistics, and a React frontend. All of it is self-hosted on the same server as this site.",
|
||||
"BULLET_1": "Steam sync of owned games, playtime, achievements and wishlist, by hand and every night, plus time to beat from IGDB.",
|
||||
"BULLET_2": "\"What to play next\": seven rows of suggestions, such as the next part of a series, the same studio, or games that people you follow finished, filtered by a time budget and a mood.",
|
||||
"SHORT_DESCRIPTION": "A backlog tracker for Steam and PlayStation that helps decide what to play next.",
|
||||
"INTRODUCTION": "My game library has far more games than I will ever finish. To Be Played syncs Steam and PlayStation, imports lists from GOG, Playnite, Grouvee and HowLongToBeat, keeps the backlog with status, rating and notes, and suggests what to play next for the time I have. Behind it are three services: an auth service that signs the login tokens, a core service for the library and the statistics, and a React frontend. All of it is self-hosted on the same server as this site.",
|
||||
"BULLET_1": "Steam sync of owned games, playtime, achievements and wishlist, PlayStation sync of titles, playtime and trophies, by hand and every night, plus time to beat from IGDB.",
|
||||
"BULLET_2": "\"What to play next\": seven rows of three suggestions, such as the next part of a series, the same studio, or games that people you follow played or finished. A time budget filters every row; the mood filter only shapes the random picks.",
|
||||
"BULLET_3": "Public profiles, following other players with an inbox of their progress, and Pile Points for every game played off the pile.",
|
||||
"BULLET_4": "German and English, light and dark mode, and it works on the phone. Auth, Core and Frontend each ship as their own Docker image.",
|
||||
"CHALLENGE_1": "Login across two services. Auth signs the tokens with an Ed25519 key, and Core only holds the public key, so Core can check a login but never create one. The tokens live in HttpOnly cookies, so no script on the page can read them.",
|
||||
"CHALLENGE_2": "Steam's limits. Every Steam call is rate limited and retried, and a sync first claims its run in the database, so two syncs of the same user never run at the same time.",
|
||||
"BULLET_5": "Over 3,400 tests across the three services, ArchUnit rules that keep the layers apart, and Testcontainers for the real database. CI runs the full build and tags every image by commit.",
|
||||
"CHALLENGE_1": "Login across two services. Auth signs the tokens with an Ed25519 key, and Core only holds the public key, so Core can check a login but never create one. Core fetches that key from Auth at runtime, looked up by key id, and the tokens live in HttpOnly cookies, so no script on the page can read them.",
|
||||
"CHALLENGE_2": "Steam's limits. Every Steam call is rate limited to two per second and retried only where a retry is safe (owned games, schema, achievements). A sync first claims its run in the database, so two syncs of the same user never run at the same time, and the nightly sync only touches users seen in the last 14 days.",
|
||||
"CHALLENGE_3": "Suggesting a game is harder than it sounds. Each row of suggestions needs its own rules, and the lists behind every number must use the same rules as the number, so a game counts the same way everywhere.",
|
||||
"LEARNING_1": "Splitting an app into services only pays off with a clear contract. The token claims are pinned in a contract file that both services test against, and a script checks that both copies match.",
|
||||
"LEARNING_2": "A hobby project still needs the boring parts: backups, migrations and a fixed deploy order. They decided more about how stable it runs than any feature did."
|
||||
"CHALLENGE_4": "Login abuse. Too many failed logins get a 429 with Retry-After. An unknown email still pays for a full BCrypt check, so the timing gives nothing away. A header guard blocks CSRF, CORS allows exact origins only, and the PlayStation token is encrypted at rest.",
|
||||
"LEARNING_1": "Splitting an app into services only pays off with a clear contract. The token claims are pinned in a contract file that both services test against, and a script I run by hand checks that both copies match.",
|
||||
"LEARNING_2": "A hobby project still needs the boring parts: daily database dumps kept for seven days, four weeks and six months, migrations, and a written deploy order. They decided more about how stable it runs than any feature did."
|
||||
}
|
||||
},
|
||||
"IMPRINT": {
|
||||
|
||||
Reference in new issue
Block a user