Move Internal Headers and Symbols Out of the Public Include Surface
Summary
The project currently installs several headers from include/ that expose implementation details such as concrete struct afp_server, struct afp_volume, DSI request queues, mutexes, file descriptors, reconnect state, and low-level AFP protocol helpers. This makes the public API harder to consume from C/C++, leaks unstable ABI details, and turns internal synchronization choices into public header dependencies.
We should split the public API from internal implementation headers. Public headers should expose stable handles, value structs, constants, and supported functions. Internal headers and symbols should live under lib/ and be included only by in-tree implementation code.
Motivation
- Avoid exposing implementation details such as
pthread_mutex_t, reconnect guards, request queues, fd state, and AFP session internals.
- Avoid public C++ compatibility issues from internal C constructs or compiler-specific synchronization details.
- Make ABI and API expectations clearer before the project leaves alpha.
- Reduce accidental dependency by external consumers on structs and functions we may need to change freely.
- Make future concurrency fixes easier by keeping threading primitives private.
Current Problem Areas
include/afp.h exposes concrete internal structs and many low-level functions.
struct afp_server is public even though callers should generally use serverid_t or library functions.
struct afp_volume is public even though stateless users generally use volumeid_t.
- Internal helpers such as server lookup, reconnect, AFP command functions, cache helpers, loop helpers, and protocol reply functions are visible through public headers.
- Some headers under
include/ are effectively internal, including dsi.h, midlevel.h, utils.h, and parts of map_def.h.
Proposed Direction
-
Keep public headers minimal and stable:
include/afp.h
include/afpsl.h
include/afp_protocol.h
include/afp_xattr.h
include/libafpclient.h
- any wire protocol IPC headers that must remain public
-
Move implementation-only declarations to lib/:
lib/afp_internal.h
lib/dsi.h
lib/midlevel.h
lib/utils.h or a narrower internal utility header
- protocol/private reply headers as needed
-
Make server and volume opaque to public consumers:
- Public:
struct afp_server;
struct afp_volume;
typedef void *serverid_t;
typedef void *volumeid_t;
- Internal:
concrete struct afp_server and struct afp_volume definitions.
-
Keep public value structs public:
struct afp_url
struct afp_server_basic
struct afp_file_info_basic
struct afp_volume_summary
Suggested Migration Plan
Phase 1: Inventory
- List all installed headers in
include/meson.build.
- Classify each header as public, private, or mixed.
- For mixed headers, list which declarations are actually needed by external users.
Phase 2: Introduce Internal Header
- Add
lib/afp_internal.h.
- Move concrete internal structs there:
struct afp_server
struct afp_volume
struct afp_file_info
struct afp_rx_buffer
struct afp_token
- internal comment/icon structs if not public
- Move private macros and state constants there:
SERVER_STATE_*
- mount/attach state constants
volume_is_readonly
- reconnect guard helpers
Phase 3: Trim Public afp.h
- Keep only public declarations in
include/afp.h.
- Remove concrete server/volume structs.
- Remove low-level AFP command prototypes.
- Remove internal synchronization, reconnect, queue, and loop details.
- Ensure
afp.h is self-contained and usable from C and C++.
Phase 4: Move Internal Headers
- Move implementation-only headers from
include/ to lib/ where appropriate.
- Update in-tree includes to reference internal headers directly.
- Keep compatibility shims only if needed temporarily, and do not install them.
Phase 5: Tighten Installation
- Update
include/meson.build so only public headers are installed.
- Verify
meson install does not install internal headers.
- Consider adding a simple CI check that installed headers do not include
lib/ internals or expose internal-only symbols.
Phase 6: Compile and Consumer Checks
- Build the full project.
- Run the test suite.
- Add lightweight public-header compile checks:
- C translation unit including public headers only.
- C++ translation unit including public headers only.
- Verify consumers can use stateless APIs without
AFPCLIENT_INTERNAL.
Acceptance Criteria
- Public headers no longer expose
struct afp_server internals.
- Public headers no longer expose
struct afp_volume internals.
- Public headers do not expose threading primitives, atomics, DSI queues, fd state, or reconnect state.
- Internal implementation still builds using headers under
lib/.
- Installed headers are self-contained and compile in both C and C++.
- Existing Meson tests pass.
- No external consumer needs
AFPCLIENT_INTERNAL for supported public APIs.
Non-Goals
- Do not redesign the stateless IPC protocol in this issue.
- Do not remove existing public stateless APIs.
- Do not change runtime behavior except where needed to preserve build correctness after the header split.
- Do not make ABI stability guarantees beyond reducing public exposure of unstable internals.
Open Questions
- Should low-level stateful AFP APIs remain public at all, or should the supported public API be the stateless
afpsl surface?
- Should
include/afp_server.h remain public, or is it daemon IPC only?
- Should public handles remain
void *, or should they become typed opaque pointers?
- Do we want temporary compatibility headers for one release, or can alpha status allow an immediate cleanup?
Notes
This cleanup was prompted by reconnect state synchronization work. Exposing synchronization primitives or atomic storage in public structs creates unnecessary C/C++ compatibility and ABI concerns. Keeping those details internal lets us fix threading issues without making implementation choices part of the public API.
Move Internal Headers and Symbols Out of the Public Include Surface
Summary
The project currently installs several headers from
include/that expose implementation details such as concretestruct afp_server,struct afp_volume, DSI request queues, mutexes, file descriptors, reconnect state, and low-level AFP protocol helpers. This makes the public API harder to consume from C/C++, leaks unstable ABI details, and turns internal synchronization choices into public header dependencies.We should split the public API from internal implementation headers. Public headers should expose stable handles, value structs, constants, and supported functions. Internal headers and symbols should live under
lib/and be included only by in-tree implementation code.Motivation
pthread_mutex_t, reconnect guards, request queues, fd state, and AFP session internals.Current Problem Areas
include/afp.hexposes concrete internal structs and many low-level functions.struct afp_serveris public even though callers should generally useserverid_tor library functions.struct afp_volumeis public even though stateless users generally usevolumeid_t.include/are effectively internal, includingdsi.h,midlevel.h,utils.h, and parts ofmap_def.h.Proposed Direction
Keep public headers minimal and stable:
include/afp.hinclude/afpsl.hinclude/afp_protocol.hinclude/afp_xattr.hinclude/libafpclient.hMove implementation-only declarations to
lib/:lib/afp_internal.hlib/dsi.hlib/midlevel.hlib/utils.hor a narrower internal utility headerMake server and volume opaque to public consumers:
concrete
struct afp_serverandstruct afp_volumedefinitions.Keep public value structs public:
struct afp_urlstruct afp_server_basicstruct afp_file_info_basicstruct afp_volume_summarySuggested Migration Plan
Phase 1: Inventory
include/meson.build.Phase 2: Introduce Internal Header
lib/afp_internal.h.struct afp_serverstruct afp_volumestruct afp_file_infostruct afp_rx_bufferstruct afp_tokenSERVER_STATE_*volume_is_readonlyPhase 3: Trim Public
afp.hinclude/afp.h.afp.his self-contained and usable from C and C++.Phase 4: Move Internal Headers
include/tolib/where appropriate.Phase 5: Tighten Installation
include/meson.buildso only public headers are installed.meson installdoes not install internal headers.lib/internals or expose internal-only symbols.Phase 6: Compile and Consumer Checks
AFPCLIENT_INTERNAL.Acceptance Criteria
struct afp_serverinternals.struct afp_volumeinternals.lib/.AFPCLIENT_INTERNALfor supported public APIs.Non-Goals
Open Questions
afpslsurface?include/afp_server.hremain public, or is it daemon IPC only?void *, or should they become typed opaque pointers?Notes
This cleanup was prompted by reconnect state synchronization work. Exposing synchronization primitives or atomic storage in public structs creates unnecessary C/C++ compatibility and ABI concerns. Keeping those details internal lets us fix threading issues without making implementation choices part of the public API.