diff --git a/README.md b/README.md index f878e41..9225702 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,11 @@ flicknote daemon restart `flicknote login` authenticates and then installs, starts, and verifies the user daemon. `flicknote logout` stops and uninstalls it before clearing the session and local database. -Use `--force` only for explicit recovery when cleanup cannot be confirmed: +After upgrading an existing dev installation for the cnsupa authentication cutover, +run `flicknote login --force` once. This stops and uninstalls the existing daemon, +replaces the old session, and installs, starts, and verifies the daemon again. It +does not delete the local database. `flicknote logout --force` is reserved for +explicit recovery when service cleanup cannot be confirmed: ```bash flicknote login --force @@ -180,6 +184,14 @@ Environment variables: - `FLICKNOTE_API_URL` — API Worker base URL for share links - `FLICKNOTE_GATEWAY_URL` — Gateway origin for attachment operations and `gateway request` +For the default `dev` environment, the built-in `FLICKNOTE_SUPABASE_KEY` value +in the [runtime configuration](flicknote-core/src/config.rs) is an opaque cnsupa +publishable key, not the retired JWT-shaped anon key. The value is sent through +Supabase's existing `apikey` header. Existing dev users must upgrade and run +`flicknote login --force` once to replace the old session before normal sync; +explicit config-file and environment key overrides continue to work for custom +environments. + `apiUrl` and `gatewayUrl` can also be set in `config.json`. After changing either value, restart the daemon with `flicknote daemon restart`. Configure the two endpoint values together; setting only one is rejected. diff --git a/docs/daemon.md b/docs/daemon.md index 11d2694..faa9529 100644 --- a/docs/daemon.md +++ b/docs/daemon.md @@ -42,6 +42,12 @@ local state while reporting the unresolved service cleanup. removing the old session. A failed forced authentication does not restore the old session or service. +After upgrading an existing dev installation for the cnsupa cutover, run +`flicknote login --force` once before normal sync. The forced login then +authenticates with the opaque publishable key and installs, starts, and verifies +the daemon through the existing lifecycle. It does not automatically delete the +local PowerSync database or perform a release or deployment. + ## Service commands The same commands select a user-level launchd service on macOS and a user-level diff --git a/flicknote-core/src/config.rs b/flicknote-core/src/config.rs index f03a31e..cd7d535 100644 --- a/flicknote-core/src/config.rs +++ b/flicknote-core/src/config.rs @@ -218,7 +218,7 @@ fn builtin_defaults(env: &str) -> EndpointDefaults { ), _ => ( "https://dev-auth.flicknote.app", - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlIiwiaWF0IjoxNzY1NTM1NTg4LCJleHAiOjE5MjMyMTU1ODh9.7ErMPvghlVm6mew-IKjSShP1Lf6wTCbNgs9ufuh3yqo", + "sb_publishable_4VEs5DX9YlkHuViFbmRMQb_f_LPrdOR", "https://dev-sync.flicknote.app", "https://dev-api.flicknote.app/api/v1", "https://dev-gw.flicknote.app", @@ -281,7 +281,32 @@ mod tests { assert_eq!(defaults.powersync_url, "https://dev-sync.flicknote.app"); assert_eq!(defaults.api_url, "https://dev-api.flicknote.app/api/v1"); assert_eq!(defaults.gateway_url, "https://dev-gw.flicknote.app"); - assert!(!defaults.supabase_anon_key.is_empty()); + assert_eq!( + defaults.supabase_anon_key, + "sb_publishable_4VEs5DX9YlkHuViFbmRMQb_f_LPrdOR" + ); + } + + #[test] + fn test_config_load_uses_dev_defaults_without_config() { + with_clean_env(None, || { + let config_home = tempfile::tempdir().expect("config tempdir"); + let data_home = tempfile::tempdir().expect("data tempdir"); + unsafe { + std::env::set_var("XDG_CONFIG_HOME", config_home.path()); + std::env::set_var("XDG_DATA_HOME", data_home.path()); + } + + let config = Config::load().expect("Config::load should use dev defaults"); + assert_eq!(config.supabase_url, "https://dev-auth.flicknote.app"); + assert_eq!( + config.supabase_anon_key, + "sb_publishable_4VEs5DX9YlkHuViFbmRMQb_f_LPrdOR" + ); + assert_eq!(config.powersync_url, "https://dev-sync.flicknote.app"); + assert_eq!(config.api_url, "https://dev-api.flicknote.app/api/v1"); + assert_eq!(config.gateway_url, "https://dev-gw.flicknote.app"); + }); } #[test] @@ -304,6 +329,7 @@ mod tests { fn test_env_var_overrides_builtin() { with_clean_env(None, || { unsafe { std::env::set_var("FLICKNOTE_SUPABASE_URL", "https://custom.example.com") }; + unsafe { std::env::set_var("FLICKNOTE_SUPABASE_KEY", "custom-publishable-key") }; unsafe { std::env::set_var( "XDG_CONFIG_HOME", @@ -324,6 +350,7 @@ mod tests { }; let cfg = Config::load().expect("Config::load should succeed"); assert_eq!(cfg.supabase_url, "https://custom.example.com"); + assert_eq!(cfg.supabase_anon_key, "custom-publishable-key"); }); } @@ -351,6 +378,7 @@ mod tests { }; let cfg = Config::load().expect("Config::load should succeed"); assert_eq!(cfg.supabase_url, "https://file.example.com"); + assert_eq!(cfg.supabase_anon_key, "key"); assert_eq!(cfg.api_url, "https://api.example.com/v1"); assert_eq!(cfg.gateway_url, "https://gateway.example.com"); });