Let a vault manifest import other vaults by ref (cross-repo secret composition) #113

Description

@ineedjet

Problem

vaults/*.yml can currently only source values from the current repo's
own secrets/vars GitHub context (GITHUB_SECRETS_JSON/GITHUB_VARS_JSON
in encrypt-env). There's no way for a vault to pull in a value that's
only known to a different repository — e.g. a third repo holds a
Telegram bot token as its own secret, and several other repos' vaults need
to include that value without each of them independently knowing the raw
secret.

If the source repo is in the same GitHub organization, this is already
solved natively by GitHub Organization Secrets (shared across repos by
policy) — no new flightdeck mechanism needed for that case. This issue is
specifically for when that's not enough: a different org, or a value
that's produced by another repo's own vault-preparation pipeline rather
than being a static GH secret.

Proposal

Let a vault manifest declare other already-published, encrypted vault
assets to import:

asset: hawkeye.sops.envkeys:
- hawkeyevaults:
- owner/repo3@latest:shared.sops.envenv:
APPS_DOMAIN: RUBYKATZEN_COM_DOMAINTELEGRAM_BOT_TOKEN: TELEGRAM_BOT_TOKEN # sourced from the imported vault

At encrypt time, before rendering env:, encrypt-env would download each
vaults: entry's asset, decrypt it, parse it as dotenv, and add its
key=value pairs into the pool of sources available to render_env()'s
resolution — alongside (not replacing) GITHUB_SECRETS_JSON/
GITHUB_VARS_JSON.

Mechanics / open questions to resolve when designing this

  • A new CI-side decrypt key is required. No private age key material
    exists in CI today — the server's own private key
    (flightdeck_sops_age_key_file) only ever lives on the target server,
    never in CI. Importing a vault means decrypting it during the CI build
    step, which needs its own dedicated keypair: the source vault
    (owner/repo3) encrypts for this recipient specifically, and the
    private half lives as a CI secret in whichever repo(s) perform the
    import (a new encrypt-env input, e.g. import-age-key). This is a
    genuinely new trust boundary, distinct from the existing keys: (who
    the output vault is encrypted for) — worth being deliberate about, not
    bolted on casually.
  • Resolution precedence. Today it's secrets > variables. Adding
    imported-vault values as a third source needs an explicit rule: does an
    imported value win over this repo's own secret of the same source name,
    or lose? Or is a name collision here an error, matching the fail-loud
    philosophy already used for env_refs merging in ansible/deploy.yml?
  • Cycle protection. If vaults can import each other by ref, a
    self-reference or cycle (A imports B, B imports A) needs at least a
    documented "don't do this." Doesn't need hard validation given the
    "keep readers dumb" philosophy used elsewhere in this repo
    (load-yaml-matrix), but worth deciding consciously rather than
    discovering it by accident.
  • Scope of what gets imported. Import brings in all of the source
    vault's decrypted key=value pairs, not a scoped subset — worth
    confirming that's actually wanted vs. letting the importing manifest
    cherry-pick specific keys.

Related

#104 (env_refs / multi-vault merging) explicitly flagged cross-repo
age-key distribution as an unresolved obstacle — this issue is the
concrete proposal for solving it. Touches .github/actions/encrypt-env
(render-env.py, action.yml) and the vault manifest schema documented
in the main README's "Vaults And Targets" section.

Not urgent — deliberately deferred, real design work needed before
implementing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
       blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
      }
      } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
      })();
      (function(){
      try {
      var __m = "github.com";
      var __re = new RegExp('^' + "github\\.com" + '
      
      Skip to content

      Let a vault manifest import other vaults by ref (cross-repo secret composition) #113

      Description

      @ineedjet

      Problem

      vaults/*.yml can currently only source values from the current repo's
      own secrets/vars GitHub context (GITHUB_SECRETS_JSON/GITHUB_VARS_JSON
      in encrypt-env). There's no way for a vault to pull in a value that's
      only known to a different repository — e.g. a third repo holds a
      Telegram bot token as its own secret, and several other repos' vaults need
      to include that value without each of them independently knowing the raw
      secret.

      If the source repo is in the same GitHub organization, this is already
      solved natively by GitHub Organization Secrets (shared across repos by
      policy) — no new flightdeck mechanism needed for that case. This issue is
      specifically for when that's not enough: a different org, or a value
      that's produced by another repo's own vault-preparation pipeline rather
      than being a static GH secret.

      Proposal

      Let a vault manifest declare other already-published, encrypted vault
      assets to import:

      asset: hawkeye.sops.envkeys:
      - hawkeyevaults:
      - owner/repo3@latest:shared.sops.envenv:
      APPS_DOMAIN: RUBYKATZEN_COM_DOMAINTELEGRAM_BOT_TOKEN: TELEGRAM_BOT_TOKEN # sourced from the imported vault

      At encrypt time, before rendering env:, encrypt-env would download each
      vaults: entry's asset, decrypt it, parse it as dotenv, and add its
      key=value pairs into the pool of sources available to render_env()'s
      resolution — alongside (not replacing) GITHUB_SECRETS_JSON/
      GITHUB_VARS_JSON.

      Mechanics / open questions to resolve when designing this

      • A new CI-side decrypt key is required. No private age key material
        exists in CI today — the server's own private key
        (flightdeck_sops_age_key_file) only ever lives on the target server,
        never in CI. Importing a vault means decrypting it during the CI build
        step, which needs its own dedicated keypair: the source vault
        (owner/repo3) encrypts for this recipient specifically, and the
        private half lives as a CI secret in whichever repo(s) perform the
        import (a new encrypt-env input, e.g. import-age-key). This is a
        genuinely new trust boundary, distinct from the existing keys: (who
        the output vault is encrypted for) — worth being deliberate about, not
        bolted on casually.
      • Resolution precedence. Today it's secrets > variables. Adding
        imported-vault values as a third source needs an explicit rule: does an
        imported value win over this repo's own secret of the same source name,
        or lose? Or is a name collision here an error, matching the fail-loud
        philosophy already used for env_refs merging in ansible/deploy.yml?
      • Cycle protection. If vaults can import each other by ref, a
        self-reference or cycle (A imports B, B imports A) needs at least a
        documented "don't do this." Doesn't need hard validation given the
        "keep readers dumb" philosophy used elsewhere in this repo
        (load-yaml-matrix), but worth deciding consciously rather than
        discovering it by accident.
      • Scope of what gets imported. Import brings in all of the source
        vault's decrypted key=value pairs, not a scoped subset — worth
        confirming that's actually wanted vs. letting the importing manifest
        cherry-pick specific keys.

      Related

      #104 (env_refs / multi-vault merging) explicitly flagged cross-repo
      age-key distribution as an unresolved obstacle — this issue is the
      concrete proposal for solving it. Touches .github/actions/encrypt-env
      (render-env.py, action.yml) and the vault manifest schema documented
      in the main README's "Vaults And Targets" section.

      Not urgent — deliberately deferred, real design work needed before
      implementing.

      Metadata

      Metadata

      Assignees

      No one assigned

        Labels

        No labels
        No labels

        Type

        No type

        Projects

        No projects

          Milestone

          No milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
          Skip to content

          Let a vault manifest import other vaults by ref (cross-repo secret composition) #113

          Description

          @ineedjet

          Problem

          vaults/*.yml can currently only source values from the current repo's
          own secrets/vars GitHub context (GITHUB_SECRETS_JSON/GITHUB_VARS_JSON
          in encrypt-env). There's no way for a vault to pull in a value that's
          only known to a different repository — e.g. a third repo holds a
          Telegram bot token as its own secret, and several other repos' vaults need
          to include that value without each of them independently knowing the raw
          secret.

          If the source repo is in the same GitHub organization, this is already
          solved natively by GitHub Organization Secrets (shared across repos by
          policy) — no new flightdeck mechanism needed for that case. This issue is
          specifically for when that's not enough: a different org, or a value
          that's produced by another repo's own vault-preparation pipeline rather
          than being a static GH secret.

          Proposal

          Let a vault manifest declare other already-published, encrypted vault
          assets to import:

          asset: hawkeye.sops.envkeys:
          - hawkeyevaults:
          - owner/repo3@latest:shared.sops.envenv:
          APPS_DOMAIN: RUBYKATZEN_COM_DOMAINTELEGRAM_BOT_TOKEN: TELEGRAM_BOT_TOKEN # sourced from the imported vault

          At encrypt time, before rendering env:, encrypt-env would download each
          vaults: entry's asset, decrypt it, parse it as dotenv, and add its
          key=value pairs into the pool of sources available to render_env()'s
          resolution — alongside (not replacing) GITHUB_SECRETS_JSON/
          GITHUB_VARS_JSON.

          Mechanics / open questions to resolve when designing this

          • A new CI-side decrypt key is required. No private age key material
            exists in CI today — the server's own private key
            (flightdeck_sops_age_key_file) only ever lives on the target server,
            never in CI. Importing a vault means decrypting it during the CI build
            step, which needs its own dedicated keypair: the source vault
            (owner/repo3) encrypts for this recipient specifically, and the
            private half lives as a CI secret in whichever repo(s) perform the
            import (a new encrypt-env input, e.g. import-age-key). This is a
            genuinely new trust boundary, distinct from the existing keys: (who
            the output vault is encrypted for) — worth being deliberate about, not
            bolted on casually.
          • Resolution precedence. Today it's secrets > variables. Adding
            imported-vault values as a third source needs an explicit rule: does an
            imported value win over this repo's own secret of the same source name,
            or lose? Or is a name collision here an error, matching the fail-loud
            philosophy already used for env_refs merging in ansible/deploy.yml?
          • Cycle protection. If vaults can import each other by ref, a
            self-reference or cycle (A imports B, B imports A) needs at least a
            documented "don't do this." Doesn't need hard validation given the
            "keep readers dumb" philosophy used elsewhere in this repo
            (load-yaml-matrix), but worth deciding consciously rather than
            discovering it by accident.
          • Scope of what gets imported. Import brings in all of the source
            vault's decrypted key=value pairs, not a scoped subset — worth
            confirming that's actually wanted vs. letting the importing manifest
            cherry-pick specific keys.

          Related

          #104 (env_refs / multi-vault merging) explicitly flagged cross-repo
          age-key distribution as an unresolved obstacle — this issue is the
          concrete proposal for solving it. Touches .github/actions/encrypt-env
          (render-env.py, action.yml) and the vault manifest schema documented
          in the main README's "Vaults And Targets" section.

          Not urgent — deliberately deferred, real design work needed before
          implementing.

          Metadata

          Metadata

          Assignees

          No one assigned

            Labels

            No labels
            No labels

            Type

            No type

            Projects

            No projects

              Milestone

              No milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
              Skip to content

              Let a vault manifest import other vaults by ref (cross-repo secret composition) #113

              Description

              @ineedjet

              Problem

              vaults/*.yml can currently only source values from the current repo's
              own secrets/vars GitHub context (GITHUB_SECRETS_JSON/GITHUB_VARS_JSON
              in encrypt-env). There's no way for a vault to pull in a value that's
              only known to a different repository — e.g. a third repo holds a
              Telegram bot token as its own secret, and several other repos' vaults need
              to include that value without each of them independently knowing the raw
              secret.

              If the source repo is in the same GitHub organization, this is already
              solved natively by GitHub Organization Secrets (shared across repos by
              policy) — no new flightdeck mechanism needed for that case. This issue is
              specifically for when that's not enough: a different org, or a value
              that's produced by another repo's own vault-preparation pipeline rather
              than being a static GH secret.

              Proposal

              Let a vault manifest declare other already-published, encrypted vault
              assets to import:

              asset: hawkeye.sops.envkeys:
              - hawkeyevaults:
              - owner/repo3@latest:shared.sops.envenv:
              APPS_DOMAIN: RUBYKATZEN_COM_DOMAINTELEGRAM_BOT_TOKEN: TELEGRAM_BOT_TOKEN # sourced from the imported vault

              At encrypt time, before rendering env:, encrypt-env would download each
              vaults: entry's asset, decrypt it, parse it as dotenv, and add its
              key=value pairs into the pool of sources available to render_env()'s
              resolution — alongside (not replacing) GITHUB_SECRETS_JSON/
              GITHUB_VARS_JSON.

              Mechanics / open questions to resolve when designing this

              • A new CI-side decrypt key is required. No private age key material
                exists in CI today — the server's own private key
                (flightdeck_sops_age_key_file) only ever lives on the target server,
                never in CI. Importing a vault means decrypting it during the CI build
                step, which needs its own dedicated keypair: the source vault
                (owner/repo3) encrypts for this recipient specifically, and the
                private half lives as a CI secret in whichever repo(s) perform the
                import (a new encrypt-env input, e.g. import-age-key). This is a
                genuinely new trust boundary, distinct from the existing keys: (who
                the output vault is encrypted for) — worth being deliberate about, not
                bolted on casually.
              • Resolution precedence. Today it's secrets > variables. Adding
                imported-vault values as a third source needs an explicit rule: does an
                imported value win over this repo's own secret of the same source name,
                or lose? Or is a name collision here an error, matching the fail-loud
                philosophy already used for env_refs merging in ansible/deploy.yml?
              • Cycle protection. If vaults can import each other by ref, a
                self-reference or cycle (A imports B, B imports A) needs at least a
                documented "don't do this." Doesn't need hard validation given the
                "keep readers dumb" philosophy used elsewhere in this repo
                (load-yaml-matrix), but worth deciding consciously rather than
                discovering it by accident.
              • Scope of what gets imported. Import brings in all of the source
                vault's decrypted key=value pairs, not a scoped subset — worth
                confirming that's actually wanted vs. letting the importing manifest
                cherry-pick specific keys.

              Related

              #104 (env_refs / multi-vault merging) explicitly flagged cross-repo
              age-key distribution as an unresolved obstacle — this issue is the
              concrete proposal for solving it. Touches .github/actions/encrypt-env
              (render-env.py, action.yml) and the vault manifest schema documented
              in the main README's "Vaults And Targets" section.

              Not urgent — deliberately deferred, real design work needed before
              implementing.

              Metadata

              Metadata

              Assignees

              No one assigned

                Labels

                No labels
                No labels

                Type

                No type

                Projects

                No projects

                  Milestone

                  No milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions

                  , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
                  Skip to content

                  Let a vault manifest import other vaults by ref (cross-repo secret composition) #113

                  Description

                  @ineedjet

                  Problem

                  vaults/*.yml can currently only source values from the current repo's
                  own secrets/vars GitHub context (GITHUB_SECRETS_JSON/GITHUB_VARS_JSON
                  in encrypt-env). There's no way for a vault to pull in a value that's
                  only known to a different repository — e.g. a third repo holds a
                  Telegram bot token as its own secret, and several other repos' vaults need
                  to include that value without each of them independently knowing the raw
                  secret.

                  If the source repo is in the same GitHub organization, this is already
                  solved natively by GitHub Organization Secrets (shared across repos by
                  policy) — no new flightdeck mechanism needed for that case. This issue is
                  specifically for when that's not enough: a different org, or a value
                  that's produced by another repo's own vault-preparation pipeline rather
                  than being a static GH secret.

                  Proposal

                  Let a vault manifest declare other already-published, encrypted vault
                  assets to import:

                  asset: hawkeye.sops.envkeys:
                  - hawkeyevaults:
                  - owner/repo3@latest:shared.sops.envenv:
                  APPS_DOMAIN: RUBYKATZEN_COM_DOMAINTELEGRAM_BOT_TOKEN: TELEGRAM_BOT_TOKEN # sourced from the imported vault

                  At encrypt time, before rendering env:, encrypt-env would download each
                  vaults: entry's asset, decrypt it, parse it as dotenv, and add its
                  key=value pairs into the pool of sources available to render_env()'s
                  resolution — alongside (not replacing) GITHUB_SECRETS_JSON/
                  GITHUB_VARS_JSON.

                  Mechanics / open questions to resolve when designing this

                  • A new CI-side decrypt key is required. No private age key material
                    exists in CI today — the server's own private key
                    (flightdeck_sops_age_key_file) only ever lives on the target server,
                    never in CI. Importing a vault means decrypting it during the CI build
                    step, which needs its own dedicated keypair: the source vault
                    (owner/repo3) encrypts for this recipient specifically, and the
                    private half lives as a CI secret in whichever repo(s) perform the
                    import (a new encrypt-env input, e.g. import-age-key). This is a
                    genuinely new trust boundary, distinct from the existing keys: (who
                    the output vault is encrypted for) — worth being deliberate about, not
                    bolted on casually.
                  • Resolution precedence. Today it's secrets > variables. Adding
                    imported-vault values as a third source needs an explicit rule: does an
                    imported value win over this repo's own secret of the same source name,
                    or lose? Or is a name collision here an error, matching the fail-loud
                    philosophy already used for env_refs merging in ansible/deploy.yml?
                  • Cycle protection. If vaults can import each other by ref, a
                    self-reference or cycle (A imports B, B imports A) needs at least a
                    documented "don't do this." Doesn't need hard validation given the
                    "keep readers dumb" philosophy used elsewhere in this repo
                    (load-yaml-matrix), but worth deciding consciously rather than
                    discovering it by accident.
                  • Scope of what gets imported. Import brings in all of the source
                    vault's decrypted key=value pairs, not a scoped subset — worth
                    confirming that's actually wanted vs. letting the importing manifest
                    cherry-pick specific keys.

                  Related

                  #104 (env_refs / multi-vault merging) explicitly flagged cross-repo
                  age-key distribution as an unresolved obstacle — this issue is the
                  concrete proposal for solving it. Touches .github/actions/encrypt-env
                  (render-env.py, action.yml) and the vault manifest schema documented
                  in the main README's "Vaults And Targets" section.

                  Not urgent — deliberately deferred, real design work needed before
                  implementing.

                  Metadata

                  Metadata

                  Assignees

                  No one assigned

                    Labels

                    No labels
                    No labels

                    Type

                    No type

                    Projects

                    No projects

                      Milestone

                      No milestone

                      Relationships

                      None yet

                      Development

                      No branches or pull requests

                      Issue actions

                      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                      Skip to content

                      Let a vault manifest import other vaults by ref (cross-repo secret composition) #113

                      Description

                      @ineedjet

                      Problem

                      vaults/*.yml can currently only source values from the current repo's
                      own secrets/vars GitHub context (GITHUB_SECRETS_JSON/GITHUB_VARS_JSON
                      in encrypt-env). There's no way for a vault to pull in a value that's
                      only known to a different repository — e.g. a third repo holds a
                      Telegram bot token as its own secret, and several other repos' vaults need
                      to include that value without each of them independently knowing the raw
                      secret.

                      If the source repo is in the same GitHub organization, this is already
                      solved natively by GitHub Organization Secrets (shared across repos by
                      policy) — no new flightdeck mechanism needed for that case. This issue is
                      specifically for when that's not enough: a different org, or a value
                      that's produced by another repo's own vault-preparation pipeline rather
                      than being a static GH secret.

                      Proposal

                      Let a vault manifest declare other already-published, encrypted vault
                      assets to import:

                      asset: hawkeye.sops.envkeys:
                      - hawkeyevaults:
                      - owner/repo3@latest:shared.sops.envenv:
                      APPS_DOMAIN: RUBYKATZEN_COM_DOMAINTELEGRAM_BOT_TOKEN: TELEGRAM_BOT_TOKEN # sourced from the imported vault

                      At encrypt time, before rendering env:, encrypt-env would download each
                      vaults: entry's asset, decrypt it, parse it as dotenv, and add its
                      key=value pairs into the pool of sources available to render_env()'s
                      resolution — alongside (not replacing) GITHUB_SECRETS_JSON/
                      GITHUB_VARS_JSON.

                      Mechanics / open questions to resolve when designing this

                      • A new CI-side decrypt key is required. No private age key material
                        exists in CI today — the server's own private key
                        (flightdeck_sops_age_key_file) only ever lives on the target server,
                        never in CI. Importing a vault means decrypting it during the CI build
                        step, which needs its own dedicated keypair: the source vault
                        (owner/repo3) encrypts for this recipient specifically, and the
                        private half lives as a CI secret in whichever repo(s) perform the
                        import (a new encrypt-env input, e.g. import-age-key). This is a
                        genuinely new trust boundary, distinct from the existing keys: (who
                        the output vault is encrypted for) — worth being deliberate about, not
                        bolted on casually.
                      • Resolution precedence. Today it's secrets > variables. Adding
                        imported-vault values as a third source needs an explicit rule: does an
                        imported value win over this repo's own secret of the same source name,
                        or lose? Or is a name collision here an error, matching the fail-loud
                        philosophy already used for env_refs merging in ansible/deploy.yml?
                      • Cycle protection. If vaults can import each other by ref, a
                        self-reference or cycle (A imports B, B imports A) needs at least a
                        documented "don't do this." Doesn't need hard validation given the
                        "keep readers dumb" philosophy used elsewhere in this repo
                        (load-yaml-matrix), but worth deciding consciously rather than
                        discovering it by accident.
                      • Scope of what gets imported. Import brings in all of the source
                        vault's decrypted key=value pairs, not a scoped subset — worth
                        confirming that's actually wanted vs. letting the importing manifest
                        cherry-pick specific keys.

                      Related

                      #104 (env_refs / multi-vault merging) explicitly flagged cross-repo
                      age-key distribution as an unresolved obstacle — this issue is the
                      concrete proposal for solving it. Touches .github/actions/encrypt-env
                      (render-env.py, action.yml) and the vault manifest schema documented
                      in the main README's "Vaults And Targets" section.

                      Not urgent — deliberately deferred, real design work needed before
                      implementing.

                      Metadata

                      Metadata

                      Assignees

                      No one assigned

                        Labels

                        No labels
                        No labels

                        Type

                        No type

                        Projects

                        No projects

                          Milestone

                          No milestone

                          Relationships

                          None yet

                          Development

                          No branches or pull requests

                          Issue actions

                          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                          Skip to content

                          Let a vault manifest import other vaults by ref (cross-repo secret composition) #113

                          Description

                          @ineedjet

                          Problem

                          vaults/*.yml can currently only source values from the current repo's
                          own secrets/vars GitHub context (GITHUB_SECRETS_JSON/GITHUB_VARS_JSON
                          in encrypt-env). There's no way for a vault to pull in a value that's
                          only known to a different repository — e.g. a third repo holds a
                          Telegram bot token as its own secret, and several other repos' vaults need
                          to include that value without each of them independently knowing the raw
                          secret.

                          If the source repo is in the same GitHub organization, this is already
                          solved natively by GitHub Organization Secrets (shared across repos by
                          policy) — no new flightdeck mechanism needed for that case. This issue is
                          specifically for when that's not enough: a different org, or a value
                          that's produced by another repo's own vault-preparation pipeline rather
                          than being a static GH secret.

                          Proposal

                          Let a vault manifest declare other already-published, encrypted vault
                          assets to import:

                          asset: hawkeye.sops.envkeys:
                          - hawkeyevaults:
                          - owner/repo3@latest:shared.sops.envenv:
                          APPS_DOMAIN: RUBYKATZEN_COM_DOMAINTELEGRAM_BOT_TOKEN: TELEGRAM_BOT_TOKEN # sourced from the imported vault

                          At encrypt time, before rendering env:, encrypt-env would download each
                          vaults: entry's asset, decrypt it, parse it as dotenv, and add its
                          key=value pairs into the pool of sources available to render_env()'s
                          resolution — alongside (not replacing) GITHUB_SECRETS_JSON/
                          GITHUB_VARS_JSON.

                          Mechanics / open questions to resolve when designing this

                          • A new CI-side decrypt key is required. No private age key material
                            exists in CI today — the server's own private key
                            (flightdeck_sops_age_key_file) only ever lives on the target server,
                            never in CI. Importing a vault means decrypting it during the CI build
                            step, which needs its own dedicated keypair: the source vault
                            (owner/repo3) encrypts for this recipient specifically, and the
                            private half lives as a CI secret in whichever repo(s) perform the
                            import (a new encrypt-env input, e.g. import-age-key). This is a
                            genuinely new trust boundary, distinct from the existing keys: (who
                            the output vault is encrypted for) — worth being deliberate about, not
                            bolted on casually.
                          • Resolution precedence. Today it's secrets > variables. Adding
                            imported-vault values as a third source needs an explicit rule: does an
                            imported value win over this repo's own secret of the same source name,
                            or lose? Or is a name collision here an error, matching the fail-loud
                            philosophy already used for env_refs merging in ansible/deploy.yml?
                          • Cycle protection. If vaults can import each other by ref, a
                            self-reference or cycle (A imports B, B imports A) needs at least a
                            documented "don't do this." Doesn't need hard validation given the
                            "keep readers dumb" philosophy used elsewhere in this repo
                            (load-yaml-matrix), but worth deciding consciously rather than
                            discovering it by accident.
                          • Scope of what gets imported. Import brings in all of the source
                            vault's decrypted key=value pairs, not a scoped subset — worth
                            confirming that's actually wanted vs. letting the importing manifest
                            cherry-pick specific keys.

                          Related

                          #104 (env_refs / multi-vault merging) explicitly flagged cross-repo
                          age-key distribution as an unresolved obstacle — this issue is the
                          concrete proposal for solving it. Touches .github/actions/encrypt-env
                          (render-env.py, action.yml) and the vault manifest schema documented
                          in the main README's "Vaults And Targets" section.

                          Not urgent — deliberately deferred, real design work needed before
                          implementing.

                          Metadata

                          Metadata

                          Assignees

                          No one assigned

                            Labels

                            No labels
                            No labels

                            Type

                            No type

                            Projects

                            No projects

                              Milestone

                              No milestone

                              Relationships

                              None yet

                              Development

                              No branches or pull requests

                              Issue actions

                              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
                              Skip to content

                              Let a vault manifest import other vaults by ref (cross-repo secret composition) #113

                              Description

                              @ineedjet

                              Problem

                              vaults/*.yml can currently only source values from the current repo's
                              own secrets/vars GitHub context (GITHUB_SECRETS_JSON/GITHUB_VARS_JSON
                              in encrypt-env). There's no way for a vault to pull in a value that's
                              only known to a different repository — e.g. a third repo holds a
                              Telegram bot token as its own secret, and several other repos' vaults need
                              to include that value without each of them independently knowing the raw
                              secret.

                              If the source repo is in the same GitHub organization, this is already
                              solved natively by GitHub Organization Secrets (shared across repos by
                              policy) — no new flightdeck mechanism needed for that case. This issue is
                              specifically for when that's not enough: a different org, or a value
                              that's produced by another repo's own vault-preparation pipeline rather
                              than being a static GH secret.

                              Proposal

                              Let a vault manifest declare other already-published, encrypted vault
                              assets to import:

                              asset: hawkeye.sops.envkeys:
                              - hawkeyevaults:
                              - owner/repo3@latest:shared.sops.envenv:
                              APPS_DOMAIN: RUBYKATZEN_COM_DOMAINTELEGRAM_BOT_TOKEN: TELEGRAM_BOT_TOKEN # sourced from the imported vault

                              At encrypt time, before rendering env:, encrypt-env would download each
                              vaults: entry's asset, decrypt it, parse it as dotenv, and add its
                              key=value pairs into the pool of sources available to render_env()'s
                              resolution — alongside (not replacing) GITHUB_SECRETS_JSON/
                              GITHUB_VARS_JSON.

                              Mechanics / open questions to resolve when designing this

                              • A new CI-side decrypt key is required. No private age key material
                                exists in CI today — the server's own private key
                                (flightdeck_sops_age_key_file) only ever lives on the target server,
                                never in CI. Importing a vault means decrypting it during the CI build
                                step, which needs its own dedicated keypair: the source vault
                                (owner/repo3) encrypts for this recipient specifically, and the
                                private half lives as a CI secret in whichever repo(s) perform the
                                import (a new encrypt-env input, e.g. import-age-key). This is a
                                genuinely new trust boundary, distinct from the existing keys: (who
                                the output vault is encrypted for) — worth being deliberate about, not
                                bolted on casually.
                              • Resolution precedence. Today it's secrets > variables. Adding
                                imported-vault values as a third source needs an explicit rule: does an
                                imported value win over this repo's own secret of the same source name,
                                or lose? Or is a name collision here an error, matching the fail-loud
                                philosophy already used for env_refs merging in ansible/deploy.yml?
                              • Cycle protection. If vaults can import each other by ref, a
                                self-reference or cycle (A imports B, B imports A) needs at least a
                                documented "don't do this." Doesn't need hard validation given the
                                "keep readers dumb" philosophy used elsewhere in this repo
                                (load-yaml-matrix), but worth deciding consciously rather than
                                discovering it by accident.
                              • Scope of what gets imported. Import brings in all of the source
                                vault's decrypted key=value pairs, not a scoped subset — worth
                                confirming that's actually wanted vs. letting the importing manifest
                                cherry-pick specific keys.

                              Related

                              #104 (env_refs / multi-vault merging) explicitly flagged cross-repo
                              age-key distribution as an unresolved obstacle — this issue is the
                              concrete proposal for solving it. Touches .github/actions/encrypt-env
                              (render-env.py, action.yml) and the vault manifest schema documented
                              in the main README's "Vaults And Targets" section.

                              Not urgent — deliberately deferred, real design work needed before
                              implementing.

                              Metadata

                              Metadata

                              Assignees

                              No one assigned

                                Labels

                                No labels
                                No labels

                                Type

                                No type

                                Projects

                                No projects

                                  Milestone

                                  No milestone

                                  Relationships

                                  None yet

                                  Development

                                  No branches or pull requests

                                  Issue actions