Option 2 is fairly common in today's projects, but it's because of the frameworks. Many support configuration overloading out of the box.
A pattern of configuration overloading by source could be *:
- Config servers
- Env vars
- Runtime properties
- Command line arguments
- Config files in the file system (local or remote)
- Packaged config files
- Hardcoded (default) configurations.
Basically, the framework loads these sources in reverse order. Each source adds new entries to the configuration or replaces existing ones following last to arrive, win strategy.
The last two are the ones we use in local env while coding and testing. Others are focused on supporting more execution environments over which we have no control. For example, production. This is a good way for the IT team to set sensible configurations (e.g passwords, seeds, tokens, URL, IPs, ports, etc) without sharing this information with you. In consequence, this information is unlikely to end in any repository.
A good and reasonable alternative would be making lib's code more parametrizable. Or provide with ports and adapters, so the consumer can adapt lib's configuration to its configuration solution.
Should I be hitting yet another centralized service to get this
endpoints so no one is required to know the value?
Centralised remote configuration services are by any meaning one more failure point for which you must have a failover operation. One more service to be secured, maintained, monitored, etc.
Hiding information from developers should not be the reason to make configuration so complicated. These services make sense when you need control over the lib because you have none over the application implementing the lib.
* From highest to lowest priority. Usually, the external configuration has priority over the internal, but it is not an essential condition