cdnV2

git clone https://github.com/notamitgamer/cdnV2.git
· rebuilt hourlyOpen on GitHub
root / app / templates / gh_sync_docs.html

gh_sync_docs.html (6656B)


      1 {% extends "base.html" %}
      2 {% set page_title = 'GitHub Actions sync' %}
      3 {% set page_description = "Push any repo's files to the CDN automatically on every git push." %}
      4 {% block page_class %}prose{% endblock %}
      5 
      6 {% block content %}
      7   <h1>Auto-sync any repo to the CDN <span class="badge">GitHub Actions</span></h1>
      8   <p>Add one workflow file to a repo. On every push, its contents land in the CDN under
      9      <code>/&lt;your-github-username&gt;/&lt;repo-name&gt;/</code> — no PAT, no shared secret, and it's
     10      physically impossible for your workflow to write into anyone else's folder.</p>
     11 
     12   <h2>How it stays safe</h2>
     13   <p>Instead of a token you paste in, your repo asks GitHub itself for a short-lived
     14      <strong>OIDC identity token</strong> for the run. That token's "who and where" fields
     15      (<code>repository_owner</code>, <code>repository</code>) are stamped by GitHub's own servers from the
     16      real run context — a workflow file can't edit itself to lie about which repo it's running in.
     17      The CDN backend verifies that token's signature, reads the owner/repo straight from it, and uses
     18      <em>only that</em> to decide where your files go. Nothing in your request body or headers is trusted
     19      for the destination path.</p>
     20   <p>The same token also says <em>who</em> you are, by GitHub's permanent numeric account ID — renaming
     21      your account, or someone registering a username you dropped, doesn't change that identity.
     22      That's what the approval step below is based on.</p>
     23 
     24   <h2>1. Add the workflow</h2>
     25   <p>Create <code>.github/workflows/cdn-sync.yml</code> in the repo you want mirrored. The token is
     26      short-lived and scoped to your repo already, but <code>core.setSecret()</code> tells GitHub to mask it
     27      from the run's logs, and passing it through <code>env:</code> instead of straight into the shell
     28      command keeps it out of the process list too — worth keeping both even though the token itself
     29      is harmless if it leaks (it expires in minutes and only proves who you are, it can't push files on its own).</p>
     30 <pre>{% raw %}name: Sync to CDN
     31 
     32 on:
     33   push:
     34     branches: [ main ]
     35   workflow_dispatch:
     36 
     37 permissions:
     38   id-token: write   # required — this is what lets us prove who you are
     39   contents: read
     40 
     41 jobs:
     42   sync:
     43     runs-on: ubuntu-latest
     44     steps:
     45       - uses: actions/checkout@v7
     46 
     47       - name: Request GitHub OIDC token
     48         id: oidc
     49         uses: actions/github-script@v7
     50         with:
     51           script: |
     52             const token = await core.getIDToken('cdn.amit.is-a.dev')
     53             core.setSecret(token)
     54             core.setOutput('token', token)
     55 
     56       - name: Package repo contents
     57         run: tar --exclude='.git' -czf /tmp/payload.tar.gz .
     58 
     59       - name: Push to CDN
     60         env:
     61           OIDC_TOKEN: ${{ steps.oidc.outputs.token }}
     62         run: |
     63           curl -sS --fail-with-body -X POST https://cdn.amit.is-a.dev/api/gh-sync \
     64             -F "token=$OIDC_TOKEN" \
     65             -F "archive=@/tmp/payload.tar.gz"
     66 {% endraw %}</pre>
     67 
     68   <h2>2. Push</h2>
     69   <p>That's it — no repo secrets to create. On the next push, your files land at:</p>
     70   <pre>https://cdn.amit.is-a.dev/&lt;your-username&gt;/&lt;repo-name&gt;/</pre>
     71   <p>Need a raw link to one specific file instead of the folder listing? Append its path to the raw
     72      domain: <code>https://raw.cdn.amit.is-a.dev/&lt;your-username&gt;/&lt;repo-name&gt;/&lt;file&gt;</code>.</p>
     73 
     74   <h2>Notes</h2>
     75   <ul>
     76     <li><strong>First sync is free, later ones need approval.</strong> The first sync from a new GitHub account
     77         goes through immediately. After that, further syncs from that account are refused until the CDN owner
     78         approves it, so after your first push, ask for approval. Approved accounts sync freely from then on.
     79         Accounts that upload abusive content are banned.</li>
     80     <li><strong>No videos.</strong> Any file type is accepted <em>except</em> video, which is detected by file
     81         extension and by file contents (renaming won't help). If the repo contains one, the whole sync is
     82         rejected and nothing is uploaded. Leave videos out of the archive, for example by adding
     83         <code>--exclude='*.mp4'</code> to the <code>tar</code> command.</li>
     84     <li><strong>No rate limit, but the compressed archive can be at most 200&nbsp;MB.</strong> There's no limit on file count or on how often you sync. What protects it instead is your
     85         sign-in identity, the approval rule, bans and the no-video rule. Keep the archive small by leaving build output out, for example
     86         <code>--exclude='node_modules'</code> in the <code>tar</code> command.
     87         <a href="/documentation#rate-limits">How rate limits work</a>.</li>
     88     <li>Each sync fully replaces the contents of <em>your own</em> <code>username/repo</code> folder only
     89         (delete-then-upload), so removed files disappear from the CDN too. No other folder is ever touched.
     90         The storage keeps no version history, so a removed or overwritten file can't be brought back — keep the
     91         original in your repo.</li>
     92     <li>Change <code>branches: [ main ]</code> to whichever branch you want mirrored.</li>
     93     <li>Want it on every branch/tag push instead? Adjust the <code>on:</code> block — the destination
     94         folder is still locked to your username/repo either way.</li>
     95   </ul>
     96 
     97   <h2>If a sync is refused</h2>
     98   <p>The step now shows the server's reason in the Actions log (that's what <code>--fail-with-body</code> is for).</p>
     99   <ul>
    100     <li><code>403</code> <em>"First sync from '&lt;you&gt;' was accepted; further syncs need approval…"</em> — your account
    101         has already synced once and is waiting to be approved. Nothing is wrong with your workflow; ask the CDN owner to
    102         approve the account, then re-run the job.</li>
    103     <li><code>413</code> <em>"Archive too large…"</em> — the compressed archive is over 200&nbsp;MB. Exclude build output and dependency folders (<code>--exclude</code>) and run it again.</li>
    104     <li><code>403</code> <em>"…banned…"</em> — the account (or the network the job ran from) has been blocked.</li>
    105     <li><code>415</code> <em>"Video files are not allowed…"</em> — the message names the file; remove it from the repo
    106         or exclude it from the archive.</li>
    107     <li><code>401</code> — the identity token was missing, expired or not for this service. Check that
    108         <code>permissions: id-token: write</code> is set and that the audience is <code>cdn.amit.is-a.dev</code>.</li>
    109     
    110   </ul>
    111   <p>The full request/response details are in the <a href="/documentation#gh-sync">documentation</a>.</p>
    112 {% endblock %}