Skip to main content
GitAscii V2 renders a profile before it is viewed, commits dark and light SVGs to the user’s GitHub repository, and embeds their raw GitHub URLs in the README. GitAscii still renders during editor publication; the GitAscii GitHub Action handles subsequent refreshes. The normal README image request does not execute GitAscii’s backend or query its database. This page describes the implementation at revision f248c93. It covers the standard GitHub-native embed. Pro dynamic rules deliberately use the HTTP rendering API instead.

V2 architecture

GitAscii V2: editor publication through the backend and GitHub API, repository-based Action refreshes, and raw SVG delivery to the README Open the interactive Archify diagram for zoom, source links, dark/light themes, and export controls. You can also save the standalone HTML and open it locally. The diagram source and validation receipts are kept beside the HTML. Arrows summarize operations; Action repository reads and writes also use the GitHub API.

Ownership and stored artifacts

The branches contain different publication inputs and outputs:
gitascii.json is the default profile configuration, including widgets, global styles, slug, and revision metadata. Named profiles use gitascii_[slug].json. The gitascii branch is initially created from the default branch’s current commit, so it can also contain inherited files; it is not created as an orphan branch. See Configuration for the model and Multi-Profile for profile management.

Editing, onboarding, and publication

  1. Edit locally. The editor store updates the design and saves a browser draft. This does not itself publish a GitHub commit. Publish to GitHub sends the current widgets, styles, template, and slug as exportData to POST /api/github/commit.
  2. Authenticate and authorize. GitHub OAuth exchanges an authorization code, reads the GitHub user, and establishes the GitAscii session. Repository publication separately uses a GitHub App installation token. The backend derives the owner from the session, checks profile write access, and stamps a revision. An absent installation returns not_installed with an installation URL; the editor redirects there and the setup callback returns to the app. That callback does not publish by itself.
  3. Prepare the repository. The backend accesses username/username and discovers its default branch. On a 404, it attempts to create the profile repository using the installation token. A rejected creation request is returned as an error; successful repository creation is not guaranteed by this code path.
  4. Render and commit now. GitAscii fetches fresh profile data and calls bootstrapGitasciiBranch. It creates gitascii if needed, renders both themes with the shared engine, processes external assets, and writes the configuration plus SVG pair through GitHub’s blobs/tree/commit/ref APIs. The branch ref update uses force: false.
  5. Write integration files. The backend generates the README embed and .github/workflows/gitascii.yml on the default branch, updating each only when its content differs. These writes are separate from the SVG/config commit.
  6. Request a refresh and persist app state. It sends workflow_dispatch for gitascii.yml with ref set to the default branch, then attempts to save the configuration in GitAscii storage. Dispatch and storage failures are logged without failing an otherwise completed publication. The editor marks its draft clean only after a successful response.
The same path handles first publication and later editor updates. A usable initial SVG pair does not depend on waiting for the dispatched Action.
The current updateReadmeContent helper returns only the generated embed block. Publishing replaces the README content; it does not preserve arbitrary surrounding Markdown or merge several profile blocks. Named profile markers identify generated blocks but do not change this behavior.
Sources: editor publication handler, publication route, OAuth callback, GitHub App token helper, branch bootstrap, and README generator.

Pro profile mutations

Creating, duplicating, or restoring a Pro profile saves app state and then calls the V2 profile publisher to bootstrap its configuration and SVGs and request a refresh. This helper assumes onboarding has already installed the workflow; it does not install it or rewrite the README. Promotion writes the canonical default and the old-default backup together in one Git commit before updating app metadata. Deletion removes the named profile files and its marked README block in separate operations. These management paths are not a transaction spanning GitHub and app storage. See profile publisher and multi-profile management.

What the backend and database retain

saveProfileConfig writes profiles metadata and JSON in profile_configurations when PostgreSQL is configured, updates the process memory cache, and invalidates the HTTP SVG cache. Without a configured database, it uses Redis for configuration persistence. Normal storage reads use memory, then PostgreSQL (or Redis when no database is configured), then GitHub as a fallback. The public configuration endpoint requests preferGitHub: true. GitHub configuration lookup tries gitascii first, then legacy root and .github/ locations on main and master. These copies can diverge: the Action reads configuration from GitHub, and its SVG refresh does not synchronize configuration back into PostgreSQL. The backend/database remain important for account state, entitlements, profile management, migration tracking, and optional Pro services. Sources: profile storage, database repository, and configuration endpoint.

Workflow and Action refreshes

The generated workflow runs on ubuntu-latest, grants contents: write, has a five-minute timeout, and cancels an earlier run in the same concurrency group. It calls Igorcbraz/GitAscii/action@<ref> with secrets.GITHUB_TOKEN. The ref is GITASCII_ACTION_SHA when configured, otherwise main; deployments are not necessarily pinned to an immutable Action revision. The Action metadata selects Node 20 and the bundled action/dist/index.js, which includes the shared SVG engine. At execution time, the Action:
  1. Reads the gitascii branch and discovers gitascii.json / gitascii_[slug].json configurations through GitHub’s tree/blob APIs. It does not load profile JSON from GitAscii’s database. Missing branches or no usable configurations fail the run; onboarding must create them first.
  2. Checks the publication policy. Only scheduled runs apply the elapsed-time guard, measured against the latest branch commit date. The effective interval is the larger of refresh_minutes and the policy minimum. Without a successful OIDC policy response, the minimum falls back to 1,440 minutes; Pro’s supported minimum is 60. Manual dispatch bypasses this elapsed-time guard.
  3. Fetches fresh GitHub data using REST and GraphQL, renders both themes for all discovered profiles (or the optional profile_slug selection), and inlines permitted external assets. See Rendering and the ASCII Pipeline for engine internals.
  4. Checks that each result contains SVG markup and submits the generated SVGs together through publishAtomic. Matching Git blob hashes produce svg_unchanged; a changed branch head or applicable configuration revision produces skipped_stale. Otherwise it creates one commit and advances the branch with force: false.
  5. Optionally reports profile-scoped health results with GitHub OIDC. Pro workflows grant id-token: write; see Health Monitoring and Telemetry & Privacy.
The policy request is a remaining GitAscii dependency during refresh, with a daily fallback. It is not a dependency for serving already published SVGs. Refresh timing is therefore a configured cadence, not a freshness or uptime guarantee. Sources: workflow generator, Action metadata, Action entry point, Git operations, and OIDC policy/telemetry.

How the SVG reaches the README

The generated <picture> references stable URLs such as:
The source media queries select the theme, with the dark image as the <img> fallback. GitHub serves the committed files; GitHub image proxy/cache behavior can delay visibility of newer bytes at the same URL. No render runs because a reader opens the README. A failed refresh can leave the previous committed image available, but this is not a guarantee against repository deletion, access changes, or GitHub outages. Use GitHub README Integration for copyable embeds. The optional Pro badge makes a separate GitAscii request; it does not turn the raw profile image into an exact view counter. An enabled Pro dynamic rule for the default profile instead generates an API URL with ?dynamic=1 and follows the HTTP path.

Failure and consistency boundaries

Sources: publication route, Action data fetcher, external asset inliner, and atomic publication.

V1: verified historical rendering path

The available pre-V2 snapshot is 4e6b497, the parent of the first available GitHub-native publishing commit, 085de1f. This describes that late V1 implementation, not every earlier deployment.
  • Save: the publisher wrote configuration JSON to the profile repository’s default branch and saved an app-side copy. README embeds pointed to GitAscii HTTP SVG endpoints such as /api/[username], with theme/slug variants and cache-busting URLs in some paths. A separate contribution-snake workflow existed; it did not render the whole GitAscii profile.
  • Render and serve: on an uncached HTTP request, GitAscii loaded configuration (memory/Redis/PostgreSQL, then legacy GitHub locations), fetched GitHub data, ran renderSvg, inlined external images, and returned image/svg+xml. Missing configuration could fall back to a template.
  • Cache/proxy: the service had an optional Cloudflare Cache API layer, memory and compressed Redis SVG caches, in-flight request deduplication, ETags/304 responses, and browser/CDN cache headers. Normal responses requested a 300-second cache lifetime and CDN stale-while-revalidate; degraded results used shorter lifetimes. GitHub Camo added another cache/proxy boundary for README embeds.
  • Limitation: cold generation still depended on the GitAscii runtime, cache/storage availability, GitHub requests, and external assets before the proxy received an image. The historical service explicitly records that awaiting an edge-cache write caused cold renders to reach Camo 504 timeouts. Layered invalidation and cache lifetimes also complicated update visibility.
Historical sources: publisher, SVG service, SVG cache, and configuration storage. The HTTP renderer remains in V2 for previews and integrations; its current behavior is documented in Endpoints.

V1 versus V2

V2 moves expensive rendering out of the usual image request and makes the published result independently retrievable from the user’s repository. The tradeoff is eventually refreshed data and a publishing pipeline that can fail or partially complete. It does not remove GitAscii storage, every API request, GitHub caching, or the need to monitor refresh failures.

V1 to V2 migration and backfill

Existing users can publish through the editor, which creates the V2 artifacts directly. That route intentionally leaves legacy configuration files in place. There is also a separate migration campaign implementation:
  1. A secret-protected POST /api/internal/migration reads eligible rows from PostgreSQL’s migration_installations table, mints installation tokens, checks rate limits, and records outcomes. Eligible states are pending, failed, permissions_missing, and post_merge_pending, with fewer than five attempts.
  2. For an unmigrated repository, the engine loads the default profile through existing storage/fallbacks and fetches fresh GitHub data. If no configuration is found, it constructs a minimal stats configuration. It bootstraps gitascii and the initial SVG pair before proposing integration changes.
  3. It opens a PR from gitascii-v2-migration to the default branch with the workflow and generated README. The PR also removes root gitascii.json / gitascii_default.json from the default branch when present. This migration handles the default profile; it is not an all-slug backfill.
  4. When a workflow is already present, the engine checks the branch, both default SVGs, and the README raw URL. It may rewrite the README to repair the embed or dispatch a refresh. It returns completed only when those checks pass; otherwise it returns post_merge_pending. Existing open or closed-unmerged PRs have separate states.
Repository inspection found no production caller of upsertMigrationInstallation and no scheduled caller of the migration endpoint. Also, the batch query excludes pr_opened, so opening a PR does not by itself establish automatic post-merge verification. Queue population, scheduling, and requeueing after merge require orchestration that cannot be verified here.
The separate runRedisToPostgresBackfill utility copies legacy app data (users, settings, profiles/configurations, versions, rules, email logs, and errors) under locks. It does not publish GitHub SVGs. No invocation of that utility was found in the current application or scripts, so it is documented as available migration code rather than an automatic startup step. Sources: migration endpoint, migration engine, queue repository, and Redis-to-PostgreSQL backfill.

Verification limits

Source inspection establishes the implemented paths, not their production execution history. The repository does not establish migration rollout completion, externally configured schedulers, actual GitHub App installation permissions, deployed GITASCII_ACTION_SHA, or exact raw/Camo cache retention. Repository creation is an attempted API call, not a verified onboarding outcome. The V1 snapshot supplies concrete engineering evidence but no measured before/after reliability or cost data.