17 reasons, each a before and an after. Select the problems you have and see how many match.
A payment webhook URL or a provisioned sender number is version-controlled configuration — but you still would not want it
scrolling past in a CI log or sitting in a screenshot. Mark the property
#[Sensitive] and the console prints
******** instead.
Nothing else changes. toArray() and your application still receive the real value.
A value that was never set prints as nothing. Masking it would imply a secret exists where none does.
A property carrying #[Sensitive] is masked whatever it holds — string, int, enum. The developer said so.
Unmarked string properties whose name contains key, secret, password or token are masked as a fallback.
Six properties on one class, run through env-settings:show.
Every row below is real command output.
| Property | Type | Printed | Why |
|---|---|---|---|
| #[Sensitive] $marked_int | int | ******** | Marked — the attribute ignores type entirely. |
| $api_key | string | ******** | Name contains "key". The fallback working as intended. |
| $max_tokens | int | 8000 | Name contains "token", but it is an int — so it is shown. |
| $monkey_api_url | string | ******** | "monkey" contains "key" — hidden for no reason. |
| #[Sensitive] $marked_empty | string | (blank) | Empty even though marked. Rule 1 comes first. |
| $passphrase | string | swordfish | Not one of the four fragments — printed in full. |
monkey_api_url contains key, so a perfectly
public URL is hidden — and you are left wondering what the value was.
passphrase,
credentials and
bearer are printed in full. This is the failure that matters.
The name list is kept only so settings written before the attribute existed stay hidden. It is a guess, not a rule. One attribute removes the guesswork:
#[Sensitive] public string $passphrase,
| mode * | sandbox | live |
| currency | USD | USD |
| retry_attempts * | 1 | 5 |
| webhook_url * | ******** | ******** |
A payment webhook URL usually embeds a token.
| sms_provider * | log | vonage |
| sms_from * | ******** | ******** |
| default_channel * | log | sms |
| rate_limit_per_minute * | 100 | 200 |
A provisioned sender number is tied to the account.
Both columns read ********, yet the row keeps its
*. The diff compares the real values and masks only when printing, so you still learn that
staging and production disagree — without learning what either one is.
Masking lives in the commands, not the model.
toArray(), serialisation and every line of your
application still get the real value. Nothing breaks by marking a property.
env-settings:make can apply the attribute for you.
Each name passed to --sensitive must also appear in
--properties.
php artisan env-settings:make WebhookSettings \
--properties="endpoint:string,signing_secret:string" \
--sensitive="signing_secret"