Well,
I spent some time tonight playing around with this and I think I have it.
Some suggestions for others:
Generate the OAuth client in the Tailscale admin before anything else.
Make sure to create the tag you'll need. One per pfSense instance (and clearly, one OAuth client per pfSense instance).
Give the OAuth client the permissions you think appropriate.
Very Important: make sure that you can generate an API key with the OAuth creds. The OAuth creds are, apparently, used by the CLI to generate an API key. The latter is what does the trick in tailscale up.
Do this from the pfSense console:
curl -d "client_id=kY5Mv4h8kQ11CNTRL" -d "client_secret=tskey-client-kY5[invalidchars]CNTRL-ZXo2FfBbb[moreinvalidchars]GVT" "https://api.tailscale.com/api/v2/oauth/token"
If you don't get back something like this, you'll never be able to get it to work:
{"access_token":"tskey-api-kM[lotsofinvalidchars]NTRL-[stillmoreinvalidchars]9YevL","token_type":"Bearer","expires_in":3600,"scope":"all"}
Here's what worked for me if the above returned an API token:
/usr/local/bin/tailscale up --auth-key=tskey-client-[greekedout]GVT\?ephemeral=false\&preauthorized=true --accept-dns=false --accept-routes --advertise-exit-node --advertise-routes=192.168.211.0/24 --advertise-tags=tag:[yourtaghere]
Make sure you have the cron package installed. Then add a @reboot entry using the full path (see above). I also added a cron entry every six hours as if Tailscale is up, this command does not interrupt or reset any sessions.
I've left some bytes of the creds in these examples to make it clearer where your full creds should go. The curl command requires the escape symbol (\) in the parameters that will be passed to the control plane.
FWIW, I lost an hour or more because I had (God only knows why) set Tailscale on one pfSense instance to accept DNS. Do this and the router cannot resolve the control plane API endpoint. Dumb. And I own it.
I don't know if this "fixes" everything. But it's a lot of work and it shouldn't be necessary. Somehow, this package to be useful needs to survive reboot without the need to go to these lengths.