8000 feat: let direct_url take a secret from a command by colinhacks · Pull Request #102 · colinhacks/reylo · GitHub
[go: up one dir, main page]

Skip to content

feat: let direct_url take a secret from a command - #102

Merged
colinhacks merged 4 commits into
mainfrom
direct-url-command
Oct 24, 2026
Merged

colinhacks merged 4 commits into
mainfrom
direct-url-command

Conversation

@colinhacks
Copy link
Copy Markdown
Owner

A $(command) in direct_url is replaced by what the command prints, so the type check's database password can come from a password manager or a cloud's token command instead of a .env file:

# .reylo.yaml
direct_url: postgres://app:$(op read op://dev/db/password)@db:5432/app
  • Runs once per process (editor session, build, reylo generate), in the project's directory, through the system shell, with a 2-minute limit. A failure, an empty output or an unclosed $( is an error that names the command; a failure is retried after a minute.
  • Refreshes on a refused login. When the server refuses the login (Postgres class 28, MySQL 1045), the command runs again and the call is retried once on the fresh output, so a short-lived token such as an RDS IAM token is replaced when it expires. The same output is never tried twice. The retry wraps every call, since postgres.js logs in at its first query rather than at connect.
  • Encodes what lands in the user or password. Output between :// and the @ is percent-encoded, which both drivers decode, so a password with @, / or ? works. A command that is the whole value prints a URL, taken as printed.
  • No value becomes shell syntax. ${VAR} outside a command is substituted as before. Inside a command it is the shell's, from the environment and then the project's .env files, so a variable's value is never run.
  • Never shown: messages and the .reylo/ cache key still name the host and user only.

Docs: a paragraph and example under direct_url in Configuration.

Verified:

  • nub run typecheck, and nub run test against throwaway Postgres 18 and MySQL 8.4: 248 passed, none skipped.
  • New tests in tests/project.test.ts and tests/analyze.test.ts, including a stand-in MySQL server that rotates its accepted token.
  • By hand on a Postgres 18 server with SCRAM authentication: a password containing @, / and ? typed a query, and after the role's password was changed and the command's output with it, the session re-ran the command and typed the next query, with exactly one refused login in the server log.
  • nub run build at the root and in site/.

A $(command) in direct_url is replaced by what it prints, so a password
can come from a password manager or a cloud's token command instead of
a file. Output in the user or password is percent-encoded; a refused
login runs the command again, so an expired token is replaced.
Copilot AI balanced review requested due to automatic review settings October 24, 2026 00:18
@vercel
vercel Bot commented Oct 24, 2026 •
Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
reylo Ready Ready Preview Oct 24, 2026 12:35am UTC

Request Review

Copilot AI left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@pullfrog pullfrog Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

The refresh-on-refused-login retry isn't single-flight. When several calls are refused at once, which is what happens when a token expires under a busy session, the command runs once per call, connections leak, and the session can still end up offline for a minute.

Reviewed changes

Reviewed the whole PR: $(command) support in direct_url, and reconnecting with the command's fresh output when the server refuses the login.

  • $(command) parsing and execution: urlParts splits direct_url into text and commands. commandOutput runs each command once per process through spawnSync with a 2-minute limit. Output that lands in the userinfo is percent-encoded.
  • Variable scoping: ${VAR} is still substituted in text. Inside a command, it is left to the shell, whose environment holds the .env files overlaid by the process environment, so a value never runs as shell code.
  • Refresh on refused login: Session.#connection retries the first open, and wraps describe/query in again, which reopens on the command's fresh output when refusedLogin matches Postgres class 28 or MySQL 1045.
  • Docs and tests: a paragraph under direct_url in Configuration, and tests for parsing, encoding, shell-injection safety, error messages and token rotation against a stand-in MySQL server.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using claude-opus-5-5 | 𝕏

Comment thread src/analyze.ts Resolved
Comment thread src/project.ts Resolved
@pullfrog pullfrog Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Both earlier findings are fixed. One minor suggestion inline: after a session recovers, the editor can keep using the wrong dialect.

Reviewed changes

Reviewed the two commits since the last Pullfrog review: a single-flight reconnect, and a kept session that recovers once a failed direct_url command succeeds.

  • Made the refused-login reopen single-flight: again keeps current as a promise. The first call refused on a connection replaces it with one reopen, and every other call retries on that same reopen, so the command and the new login each run once.
  • Closed dead connections in #drop: on Unreachable/NotMysql the session now closes the connection the failing call ran on, and only if it is still the current one. A late failure never ends a connection opened since.
  • Re-resolved the project in a kept session: #reread runs openProject before each analyze() while the project's error is a command failure, and adopts the new project, engine and URL. It clears the schema and the engine's answers.
  • Added tests: two schema reads refused together after a token rotation run the command once and leave one open connection. A session opened while the command fails recovers once its failure has stood a minute.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using claude-opus-5-5 | 𝕏

Comment thread src/analyze.ts Resolved
@pullfrog pullfrog Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found. The stale-dialect finding from the last review is fixed.

Reviewed changes

Reviewed the one commit since the last Pullfrog review: the type check now names the dialect of the project a kept session recovers to.

  • Dropped the dialect captured at openProject: Project in src/mapper-server.ts no longer stores dialect. transform awaits session.analyze(sql) first, so a #reread has already run, and then reads session.project.dialect for QueryError<…>, Query<…>, moduleTypes and leadingComment.
  • Moved lexing after the analysis: session.lex now runs after analyze(), so the transform that recovers lexes with the adopted engine. The settings that come from the project file (strictParams, sqlcommenter) are still captured at open, which is right because the URL does not change them.
  • Added a test through mapperHandlers(): a project with no engine installed and a failing mysql:// command emits QueryError<"postgres"> with code 11. Once the command succeeds after the minute, it emits QueryError<"mysql"> and "many", "mysql">.

Pullfrog  | View workflow run | Using claude-opus-5-5 | 𝕏

@colinhacks
colinhacks merged commit 4ade1b0 into main Oct 24, 2026
7 checks passed
@colinhacks
colinhacks deleted the direct-url-command branch October 24, 2026 00:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0