Motivation
I am developing an R package (call it pkg) which collects in a running cache some objects generated by its function calls. It is simple enough to implement the cache as a list object (call it .cache) in the pkg namespace. However, I want to prevent the user from interfering directly with the cache, which can always be exposed via the ::: syntax: pkg:::.cache.
That is, I want to protect .cache analogously to private fields in object-oriented programming. All operations on .cache should be done by internal helper functions, which are called exclusively by @exported functions with an "API flavor".
The Problem
As such, I had the idea of defining an environment object (call it .vault) in the namespace, so I can...put my .cache in my .vault.

My quandary is that an environment like .vault is one of the few things in R that work by reference. So I fear pkg:::.vault will expose .vault or its contents to modification as well as to viewing. I can alter my accessor functions like pkg:::.get_cache() so they don't enable such exposure, but all such efforts are in vain if .vault can be directly modified in place.
DO NOT RUN
By way of example, consider this dangerous (?) code below. First I examine the internal .S3MethodsClasses environment from dplyr, to "identify a target":
dplyr:::.S3MethodsClasses
#> <environment: 0x7fab9c6c5310>
ls(dplyr:::.S3MethodsClasses)
#> [1] "grouped_df" "rowwise_df"
Then I examine the rowwise_df object and deem it a worthy "target":
dplyr:::.S3MethodsClasses$rowwise_df
#> Virtual Class "rowwise_df" [package "dplyr"]
#>
#> Slots:
#>
#> Name: .Data names row.names
#> Class: list character data.frameRowLabels
#>
#> Name: .S3Class
#> Class: character
#>
#> Extends:
#> Class "tbl_df", directly
#> Class "tbl", by class "tbl_df", distance 2
# ...
Finally, I exploit the fact that unlike most objects from the dplyr namespace...
library(dplyr)
mutate <- NULL
dplyr::mutate
#> function (.data, ...)
#> {
#> UseMethod("mutate")
#> }
#> <bytecode: 0x7fab9c2f6120>
#> <environment: namespace:dplyr>
...an environment like .S3MethodsClasses points by reference, and so its contents can be easily modified in place:
# "Copy" the pointer to allow `<-` assignment.
same_pointer <- dplyr:::.S3MethodsClasses
same_pointer
#> <environment: 0x7fab9c6c5310>
# Modify in place.
same_pointer$rowwise_df <- NULL
dplyr:::.S3MethodsClasses$rowwise_df
#> NULL
And just like that, the "vault" is robbed!
Suspicions
I suspect the answer might lie here, with lockEnvironment() and friends, but the application is a bit beyond me. Perhaps I could do something .onLoad to set up the .vault environment, which would then be lock*()ed — both the .vault itself and the bindings within it — but not before making an active binding between .cache and an accessor function .get_cache(), which would populate within the pkg namespace.
Note
I am already developing a feature to terminate (helper) functions like .get_cache() when they are called outside a fellow function from the pkg namespace. Thus, the exposure of pkg:::.get_cache() will not let the user operate .get_cache() either manually or in a custom function of their own.
Canonicity
I would especially appreciate the advice of R developers experienced enough to provide a canonical answer (if any).